# Dwellir API Documentation

> Enterprise-grade blockchain RPC infrastructure documentation. API references and implementation guides for Dwellir-supported networks. Authenticate HTTP and WebSocket connections with X-Api-Key or a key in the endpoint URL path. Use lowercase x-api-key metadata for gRPC.

# Dwellir API Documentation — Full Content

Complete offline reference for Dwellir's blockchain infrastructure documentation.

- 150+ supported networks
- 99.99% uptime SLA
- JSON-RPC compatible API endpoints
- HTTP and WebSocket API key in the X-Api-Key header or endpoint URL path
- gRPC API key in lowercase x-api-key metadata

Quick Start: Create an API key at https://dashboard.dwellir.com/register
HTTP and WebSocket header authentication: X-Api-Key: YOUR_API_KEY
HTTP and WebSocket URL path authentication: https://api-{network}.n.dwellir.com/YOUR_API_KEY
gRPC metadata authentication: x-api-key: YOUR_API_KEY

Site index: https://www.dwellir.com/llms.txt
Section index: https://www.dwellir.com/docs/llms.txt
Looking for guides? https://www.dwellir.com/guides/llms-full.txt

## Home

# Home

## Documentation Overview

### Core Concepts

- **[Getting Started Guide](https://www.dwellir.com/docs/getting-started)** - Quick setup and first steps
- **[Shared Node Pricing](https://www.dwellir.com/docs/getting-started/pricing)** - Metered plans for every project
- **[Unlimited Node Pricing](https://www.dwellir.com/docs/getting-started/unlimited-node-pricing)** - Unmetered endpoints at a fixed monthly price
- **[Rate Limits](https://www.dwellir.com/docs/getting-started/rate-limits)** - Understanding and managing limits
- **[Supported Chains](https://www.dwellir.com/docs/getting-started/supported-chains)** - 150+ blockchain networks
- **[CLI](https://www.dwellir.com/docs/cli)** - Manage keys, endpoints, and usage from your terminal

### Network Guides

Browse our comprehensive guides for supported networks:

#### Ethereum & Layer 2s

- **[Base L2](https://www.dwellir.com/docs/base)** - Complete Base Layer 2 documentation with JSON-RPC and Debug API methods
- **[Arbitrum One](https://www.dwellir.com/docs/arbitrum)** - Leading Ethereum L2 with comprehensive JSON-RPC and Debug API documentation
- **[World Chain](https://www.dwellir.com/docs/world-chain)** - Human-first Ethereum L2 with World ID integration for free gas and priority blockspace
- **[Avalanche C-Chain](https://www.dwellir.com/docs/avalanche)** - High-performance platform with sub-second finality and 4,500+ TPS
- **[Polygon PoS](https://www.dwellir.com/docs/polygon)** - Ethereum-compatible Layer 2 with massive ecosystem and ultra-low fees
- **[Unichain](https://www.dwellir.com/docs/unichain)** - Uniswap's DeFi-optimized L2 with built-in MEV protection and fast finality

#### Next-Generation Blockchains

- **[Aptos](https://www.dwellir.com/docs/aptos)** - Layer 1 blockchain with parallel execution, Move VM, and sub-second finality
- **[Sui Network](https://www.dwellir.com/docs/sui)** - Object-centric blockchain with parallel execution and sub-second finality
- **[HyperLiquid](https://www.dwellir.com/docs/hyperliquid)** - High-performance perpetuals DEX chain with native order book
- **[Bittensor](https://www.dwellir.com/docs/bittensor)** - Decentralized AI network with machine learning consensus
- **[IoTeX](https://www.dwellir.com/docs/iotex)** - Leading blockchain for IoT devices and DePIN infrastructure
- **[Manta Pacific](https://www.dwellir.com/docs/manta-pacific)** - EVM rollup with archive, trace, and debug RPC coverage
- **[Manta Atlantic](https://www.dwellir.com/docs/manta-atlantic)** - Polkadot zk identity parachain with enterprise archive RPC

More network documentation coming soon. Check our [supported chains](https://www.dwellir.com/docs/getting-started/supported-chains) for the full list of available networks.

### Best Practices

- **Rate Limiting** - Understand and optimize your API usage
- **Error Handling** - Robust error handling patterns
- **Security** - Secure key management and best practices
- **Performance** - Optimization tips for production workloads

## Need Help?

Our support team is available 24/7 to help you succeed:

- **Email** - <support@dwellir.com>
- **Documentation** - You're already here!

Start building with confidence on Dwellir's enterprise-grade infrastructure.

---

## Acala RPC with Dwellir

## Why Build on Acala?

Acala is a DeFi-focused parachain on the Polkadot relay chain that provides a stablecoin (aUSD), liquid staking (LDOT), and EVM+Substrate interoperability. Built on Substrate, Acala exposes the standard JSON‑RPC namespaces developers rely on for production‑grade integrations.

### DeFi Primitives Out of the Box

- aUSD stablecoin, LDOT liquid staking, DEX and more, available through standard pallets and EVM.
- Seamless cross‑chain asset routing via XCM to and from Polkadot ecosystems.

### Familiar Tooling

- Works with polkadot.js, Subxt (Rust), and py‑substrate‑interface (Python).
- Dwellir provides globally anycasted endpoints with low‑latency routing and high availability.

### Security Inherited from Polkadot

- As parachain `2000` on Polkadot, Acala benefits from shared security and fast finality.

## Quick Start

Connect to Acala’s production endpoints.

### Installation & Setup

JavaScript (polkadot.js)
curl (HTTP JSON-RPC)
Rust (subxt)
Python (py-substrate-interface)

```ts
import { ApiPromise, WsProvider } from '@polkadot/api';

async function main() {
  const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
  const api = await ApiPromise.create({ provider });

  const [chain, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.version(),
  ]);

  console.log(`Connected to ${chain.toString()} v${version.toString()}`);

  // Subscribe to new blocks
  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    console.log(`New block #${header.number} ${header.hash.toHex()}`);
  });

  // Stop after 3 blocks
  setTimeout(async () => { await unsub(); await api.disconnect(); }, 18000);
}

main().catch(console.error);
```

```bash
curl -s https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let header = api.rpc().block_header(None).await?.expect("latest header");
    println!("Latest block: #{:?} (hash {:?})", header.number, header.hash());
    Ok(())
}
```

```python
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(
    url="wss://api-acala.n.dwellir.com/YOUR_API_KEY",
    type_registry_preset="substrate-node-template"  # use custom types if needed
)

chain = substrate.rpc_request("system_chain", [])
print(f"Connected to {chain['result']}")

head = substrate.rpc_request("chain_getFinalizedHead", [])
print(f"Finalized head: {head['result']}")
```

## Network Information

| Parameter            | Value            | Details                                |
| -------------------- | ---------------- | -------------------------------------- |
| Genesis Hash         | 0xfc41b9bd…ba64c | Verified via chain\_getBlockHash(0)    |
| Native Token         | ACA              | 12 decimals                            |
| SS58 Prefix          | 10               | Address format                         |
| Runtime Spec Version | 2300             | state\_getRuntimeVersion (Oct 9, 2025) |
| Transaction Version  | 3                | state\_getRuntimeVersion               |
| Explorer             | Subscan          | acala.subscan.io                       |

Acala runs as Polkadot parachain `2000`. Token properties and address prefix were verified using `system_properties`; genesis hash via `chain_getBlockHash(0)`; runtime versions via `state_getRuntimeVersion` on October 9, 2025.

## API Reference

Acala exposes the core Substrate RPC namespaces for node telemetry, block production, storage access, and transaction submission.

## Common Integration Patterns

### Subscribe to Finality and New Heads

```ts
const unsubFinal = await api.rpc.chain.subscribeFinalizedHeads((h) =>
  console.log(`Finalized #${h.number} ${h.hash.toHex()}`)
);
const unsubNew = await api.rpc.chain.subscribeNewHeads((h) =>
  console.log(`New #${h.number}`)
);
```

### Paginate Large Storage Scans

```ts
const keys = await api.rpc.state.getKeysPaged(
  // System.Account prefix
  '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
  100,
  '0x',
  null
);
console.log('Fetched', keys.length, 'keys');
```

### Estimate Fees Before Broadcasting

```ts
const ext = api.tx.balances.transferAllowDeath('ADDRESS', 1_000_000_000_000);
const info = await api.rpc.payment.queryInfo(ext.toHex());
console.log(`PartialFee: ${info.partialFee.toHuman()}`);
```

## Performance Best Practices

- Prefer WebSocket connections for subscriptions and multi‑round queries.
- Cache runtime metadata and type bundles; reuse ApiPromise across requests.
- Use `state_getKeysPaged` for large map scans; avoid full‑chain scans.
- Implement reconnection/backoff and share a connection pool across services.

## Troubleshooting

- Connection refused: ensure your API key is appended and outbound TCP/443 is allowed.
- Invalid SS58: addresses must use prefix `10` for Acala.
- Type errors: refresh metadata after runtime upgrades (`api.runtimeVersion`).
- Extrinsic failed: decode dispatch error via `api.registry.findMetaError`.

## Smoke Tests

Run these minimal checks against production endpoints (captured Oct 9, 2025):

```bash
# Acala RPC with Dwellir
curl -s https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"system_health","params":[]}'

# Latest block header (e.g. number ~ 9617916)
curl -s https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}'

# Finalized head hash (e.g. 0xdcdcc29c…f13e7)
curl -s https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"chain_getFinalizedHead","params":[]}'

# Runtime versions (specVersion 2300, transactionVersion 3)
curl -s https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"state_getRuntimeVersion","params":[]}'
```

## Migration Guide (from Polkadot/Kusama)

- Endpoints: replace with `https://api-acala.n.dwellir.com/YOUR_API_KEY` or `wss://api-acala.n.dwellir.com/YOUR_API_KEY`.
- Addresses: re‑encode SS58 addresses with prefix `10`.
- Types: include Acala custom types where applicable; refresh metadata after upgrades.
- Fees: re‑calibrate using `payment_queryInfo`; fee multipliers differ across chains.

## Resources & Tools

- [Acala Portal](https://acala.network)
- [Explorer](https://acala.subscan.io)
- [Substrate Developer Hub](https://docs.substrate.io/)
- [Dwellir Dashboard](https://dashboard.dwellir.com/register)

---

## author_pendingExtrinsics - Acala RPC Method

Returns all pending extrinsics currently in the transaction pool on Acala. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`author_pendingExtrinsics` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Acala
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Acala transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/acala/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/acala/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Acala RPC Method

Generate a new set of session keys on Acala. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`author_rotateKeys` is critical for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Validator Setup** - Generate initial session keys when setting up a new validator on decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Acala RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Acala:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/acala/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/acala/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Acala RPC Method

Submits a signed extrinsic to Acala and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Acala
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Acala RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://api-acala.n.dwellir.com/YOUR_API_KEY

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://api-acala.n.dwellir.com/YOUR_API_KEY');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://api-acala.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://api-acala.n.dwellir.com/YOUR_API_KEY").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/acala/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/acala/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/acala/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Acala RPC Method

Submits a fully signed extrinsic to Acala for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`author_submitExtrinsic` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Token Transfers** -- Send native tokens or assets between accounts on Acala
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Acala RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://api-acala.n.dwellir.com/YOUR_API_KEY").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/acala/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/acala/payment_queryInfo) -- Estimate fees before submitting
- `system_accountNextIndex` -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Acala RPC Method

# beefy_getFinalizedHead - Acala RPC Method

Returns the block hash of the latest BEEFY-finalized block on Acala. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`beefy_getFinalizedHead` is important for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Cross-Chain Bridges** - Verify finality proofs efficiently for bridge operations on decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Light Clients** - Verify finality without downloading full GRANDPA justifications
- **Trustless Bridges** - Generate compact finality proofs that can be verified on external chains
- **Bridge Monitoring** - Track BEEFY finality progress relative to GRANDPA finality

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest BEEFY-finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response (BEEFY Not Enabled)

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "BEEFY is not enabled on this chain"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  // Get BEEFY finalized head
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  console.log('BEEFY finalized:', beefyHead.toHex());

  // Compare with GRANDPA finalized
  const grandpaHead = await api.rpc.chain.getFinalizedHead();
  console.log('GRANDPA finalized:', grandpaHead.toHex());

  // Get block numbers for comparison
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const grandpaBlock = await api.rpc.chain.getBlock(grandpaHead);

  const beefyNum = beefyBlock.block.header.number.toNumber();
  const grandpaNum = grandpaBlock.block.header.number.toNumber();
  console.log(`BEEFY lag behind GRANDPA: ${grandpaNum - beefyNum} blocks`);
} catch (error) {
  console.error('BEEFY may not be enabled:', error.message);
}

await api.disconnect();
```

```python
import requests

def get_beefy_finalized_head():
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'beefy_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"BEEFY error: {result['error']['message']}")

    return result['result']

def get_grandpa_finalized_head():
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 2
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

try:
    beefy_hash = get_beefy_finalized_head()
    grandpa_hash = get_grandpa_finalized_head()
    print(f'BEEFY finalized: {beefy_hash}')
    print(f'GRANDPA finalized: {grandpa_hash}')
except Exception as e:
    print(f'Error: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Call beefy_getFinalizedHead via raw RPC
    let beefy_head: Value = api.rpc()
        .request("beefy_getFinalizedHead", subxt::rpc_params![])
        .await?;

    println!("BEEFY finalized: {}", beefy_head);

    // Compare with GRANDPA finalized
    let grandpa_head = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("GRANDPA finalized: {:?}", grandpa_head);

    Ok(())
}
```

## Common Use Cases

### 1. Bridge Finality Verification

Verify BEEFY finality before relaying messages on a cross-chain bridge:

```javascript
async function verifyBridgeFinality(api, targetBlockHash) {
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const beefyNumber = beefyBlock.block.header.number.toNumber();

  const targetBlock = await api.rpc.chain.getBlock(targetBlockHash);
  const targetNumber = targetBlock.block.header.number.toNumber();

  if (beefyNumber >= targetNumber) {
    console.log(`Block #${targetNumber} has BEEFY finality - safe to relay`);
    return true;
  } else {
    console.log(`Waiting: BEEFY at #${beefyNumber}, target at #${targetNumber}`);
    return false;
  }
}
```

### 2. BEEFY vs GRANDPA Finality Monitor

Track the gap between the two finality gadgets:

```javascript
async function monitorFinalityGadgets(api) {
  setInterval(async () => {
    try {
      const [beefyHead, grandpaHead] = await Promise.all([
        api.rpc.beefy.getFinalizedHead(),
        api.rpc.chain.getFinalizedHead()
      ]);

      const [beefyBlock, grandpaBlock] = await Promise.all([
        api.rpc.chain.getBlock(beefyHead),
        api.rpc.chain.getBlock(grandpaHead)
      ]);

      const beefyNum = beefyBlock.block.header.number.toNumber();
      const grandpaNum = grandpaBlock.block.header.number.toNumber();
      const lag = grandpaNum - beefyNum;

      console.log(`GRANDPA: #${grandpaNum} | BEEFY: #${beefyNum} | Lag: ${lag} blocks`);
    } catch (error) {
      console.error('Monitor error:', error.message);
    }
  }, 12000);
}
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/acala/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Acala RPC Method

Retrieves complete block information from Acala, including the block header, extrinsics, and justifications.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Acala RPC Method
curl https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/acala/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/acala/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Acala RPC Method

Returns the block hash for a given block number on Acala. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`chain_getBlockHash` is fundamental for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Acala
- **Block Navigation** - Navigate the blockchain history for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Acala
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Acala for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Acala state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Acala network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/acala/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Acala RPC Method

Returns the hash of the last finalized block on Acala. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`chain_getFinalizedHead` is critical for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Acala
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Acala RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://api-acala.n.dwellir.com/YOUR_API_KEY', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Acala:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/acala/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/acala/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHeader - Acala RPC Method

Returns the block header for a given hash on Acala. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`chain_getHeader` is ideal for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Acala
- **Chain Synchronization** - Track block production and monitor chain progress for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Acala:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Acala chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Acala block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/acala/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/acala/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_subscribeFinalizedHeads - Acala RPC Method

Subscribe to receive notifications when blocks are finalized on Acala. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Exchange Deposits** - Only credit funds after finalization for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://api-acala.n.dwellir.com/YOUR_API_KEY`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Acala RPC Method
wscat -c wss://api-acala.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://api-acala.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Acala:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/acala/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/acala/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Acala RPC Method

Subscribe to receive notifications when new block headers are produced on Acala. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`chain_subscribeNewHeads` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Block Monitoring** - Track new blocks in real time on Acala for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://api-acala.n.dwellir.com/YOUR_API_KEY`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Acala RPC Method
wscat -c wss://api-acala.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://api-acala.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Acala as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Acala:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/acala/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/acala/chain_getBlock) - Get full block data with extrinsics
- `chain_unsubscribeNewHeads` - Unsubscribe from new heads

---

## grandpa_roundState - Acala RPC Method

Returns the state of the current GRANDPA finality round on Acala when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`grandpa_roundState` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Finality Monitoring** -- Track whether GRANDPA rounds are progressing normally or stalling on Acala, critical for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Consensus Health Checks** -- Detect finality delays by comparing prevote/precommit counts against the supermajority threshold weight
- **Validator Participation Analysis** -- Monitor which validators are actively voting and whether the authority set has sufficient online weight
- **Authority Set Tracking** -- Observe `setId` changes after validator set rotations to verify smooth authority transitions
- **Capability Detection** -- Confirm whether the shared endpoint exposes GRANDPA round internals before you build monitoring around them

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

- `setId` (`u64, required`): The current GRANDPA authority set ID; increments when the validator set changes
- `best` (`RoundState, required`): State of the best (most recent) active round
- `background` (`Vec<RoundState>, required`): Background rounds that are still being tracked (typically the previous round)
- `round` (`u64, required`): The round number
- `totalWeight` (`u64, required`): Total combined weight of all authorities in this set
- `thresholdWeight` (`u64, required`): Minimum weight required for a supermajority (2/3 + 1 of totalWeight)
- `prevotes` (`Votes, required`): Current prevote state for this round
- `precommits` (`Votes, required`): Current precommit state for this round
- `currentWeight` (`u64, required`): Total weight of votes received so far
- `missing` (`Vec<AuthorityId>, required`): List of authority public keys that have not yet voted

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "setId": 4821,
    "best": {
      "round": 19384,
      "totalWeight": 297,
      "thresholdWeight": 199,
      "prevotes": {
        "currentWeight": 297,
        "missing": []
      },
      "precommits": {
        "currentWeight": 264,
        "missing": [
          "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
          "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
        ]
      }
    },
    "background": []
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  const roundState = await api.rpc.grandpa.roundState();
  const best = roundState.best;

  console.log('Authority set ID:', roundState.setId.toString());
  console.log('Round:', best.round.toString());
  console.log('Total weight:', best.totalWeight.toString());
  console.log('Threshold weight:', best.thresholdWeight.toString());
  console.log('Prevote weight:', best.prevotes.currentWeight.toString());
  console.log('Precommit weight:', best.precommits.currentWeight.toString());
  console.log('Missing precommits:', best.precommits.missing.length);
} catch (error) {
  console.log('grandpa_roundState unsupported:', error.message);
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('grandpa_roundState unsupported:', payload.error.message);
} else {
  console.log('Set ID:', payload.result.setId);
  console.log('Best round:', payload.result.best.round);
  console.log('Prevote progress:', payload.result.best.prevotes.currentWeight, '/', payload.result.best.thresholdWeight);
  console.log('Precommit progress:', payload.result.best.precommits.currentWeight, '/', payload.result.best.thresholdWeight);
}
```

```python
import requests

def get_grandpa_round_state():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'grandpa_roundState',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

try:
    state = get_grandpa_round_state()
    best = state['best']

    print(f"Authority set ID: {state['setId']}")
    print(f"Round: {best['round']}")
    print(f"Prevote: {best['prevotes']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Precommit: {best['precommits']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Missing precommit voters: {len(best['precommits']['missing'])}")
except KeyError:
    print('grandpa_roundState unsupported on this endpoint')

# grandpa_roundState - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
response = substrate.rpc_request('grandpa_roundState', [])
if 'error' in response:
    print(f"grandpa_roundState unsupported: {response['error']['message']}")
else:
    print(f"Set ID: {response['result']['setId']}, Round: {response['result']['best']['round']}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
    set_id: u64,
    best: BestRound,
    background: Vec<BestRound>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct BestRound {
    round: u64,
    total_weight: u64,
    threshold_weight: u64,
    prevotes: Votes,
    precommits: Votes,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Votes {
    current_weight: u64,
    missing: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "grandpa_roundState",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    if body.get("error").is_some() {
        println!("grandpa_roundState unsupported: {}", body["error"]["message"]);
        return Ok(());
    }

    let state: RoundState = serde_json::from_value(body["result"].clone())?;

    println!("Set ID: {}", state.set_id);
    println!("Round: {}", state.best.round);
    println!("Prevote: {}/{}", state.best.prevotes.current_weight, state.best.threshold_weight);
    println!("Precommit: {}/{}", state.best.precommits.current_weight, state.best.threshold_weight);
    println!("Missing precommit voters: {}", state.best.precommits.missing.len());

    Ok(())
}
```

## Common Use Cases

### 1. Finality Health Monitoring

Periodically check whether GRANDPA rounds are progressing and alert on stalls:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorFinality(api, intervalMs = 10000) {
  let lastRound = 0;
  let lastSetId = 0;
  let stallCount = 0;

  setInterval(async () => {
    const state = await api.rpc.grandpa.roundState();
    const best = state.best;
    const round = best.round.toNumber();
    const setId = state.setId.toNumber();
    const prevoteProgress = best.prevotes.currentWeight.toNumber();
    const precommitProgress = best.precommits.currentWeight.toNumber();
    const threshold = best.thresholdWeight.toNumber();

    if (setId !== lastSetId) {
      console.log(`Authority set changed: ${lastSetId} -> ${setId}`);
      lastSetId = setId;
    }

    if (round === lastRound) {
      stallCount++;
      if (stallCount >= 3) {
        console.warn(`GRANDPA round ${round} stalled for ${stallCount} checks`);
        console.warn(`  Prevotes: ${prevoteProgress}/${threshold}`);
        console.warn(`  Precommits: ${precommitProgress}/${threshold}`);
        console.warn(`  Missing voters: ${best.precommits.missing.length}`);
      }
    } else {
      stallCount = 0;
      console.log(`Round ${round} | prevotes=${prevoteProgress}/${threshold} precommits=${precommitProgress}/${threshold}`);
    }

    lastRound = round;
  }, intervalMs);
}
```

### 2. Validator Participation Report

Generate a report of which validators are consistently missing votes:

```javascript
async function trackMissingVoters(api, samples = 20, delayMs = 6000) {
  const missingCounts = {};

  for (let i = 0; i < samples; i++) {
    const state = await api.rpc.grandpa.roundState();
    const missing = state.best.precommits.missing;

    missing.forEach((authority) => {
      const key = authority.toString();
      missingCounts[key] = (missingCounts[key] || 0) + 1;
    });

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  // Sort by most frequently missing
  const sorted = Object.entries(missingCounts)
    .sort(([, a], [, b]) => b - a);

  console.log('Validator participation report:');
  sorted.forEach(([authority, count]) => {
    const missRate = ((count / samples) * 100).toFixed(1);
    console.log(`  ${authority}: missed ${count}/${samples} (${missRate}%)`);
  });

  return sorted;
}
```

### 3. Supported-Fallback Check

If the endpoint does not expose GRANDPA round internals, fall back to finalized-head tracking:

```javascript
async function getFinalitySignal(api) {
  try {
    return { supported: true, roundState: await api.rpc.grandpa.roundState() };
  } catch (error) {
    return {
      supported: false,
      finalizedHead: (await api.rpc.chain.getFinalizedHead()).toHex(),
      message: error.message
    };
  }
}
```

### 3. Finality Lag Detection

Compare the finalized head with the best block to measure finality lag:

```javascript
async function getFinalityLag(api) {
  const [roundState, finalizedHash, bestHeader] = await Promise.all([
    api.rpc.grandpa.roundState(),
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader()
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);
  const bestNumber = bestHeader.number.toNumber();
  const finalizedNumber = finalizedHeader.number.toNumber();
  const lag = bestNumber - finalizedNumber;

  return {
    bestBlock: bestNumber,
    finalizedBlock: finalizedNumber,
    lagBlocks: lag,
    grandpaRound: roundState.best.round.toNumber(),
    setId: roundState.setId.toNumber(),
    prevoteReached: roundState.best.prevotes.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber(),
    precommitReached: roundState.best.precommits.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber()
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/acala/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/acala/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/acala/system_health) -- Check overall node health including sync and peer status

---

## payment_queryFeeDetails - Acala RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Acala. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`payment_queryFeeDetails` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Acala
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/acala/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/acala/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/acala/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/acala/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Acala RPC Method

Estimates the fee for an encoded extrinsic on Acala. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`payment_queryInfo` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Acala
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Acala RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/acala/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/acala/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/acala/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/acala/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Acala RPC Method

Returns a list of all RPC methods exposed by the Acala node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`rpc_methods` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Acala node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Acala endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://api-acala.n.dwellir.com/YOUR_API_KEY', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Acala endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/acala/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/acala/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Acala RPC Method

Calls a runtime API function on Acala and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_call` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/acala/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/acala/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/acala/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/acala/system_version) -- Get the node version for compatibility checking

---

## state_getKeysPaged - Acala RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Acala. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_getKeysPaged` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Acala
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://api-acala.n.dwellir.com/YOUR_API_KEY";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/acala/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/acala/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getMetadata - Acala RPC Method

Returns the runtime metadata for Acala as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_getMetadata` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Acala
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Acala RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Acala:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Acala:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/acala/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/acala/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) - Call runtime APIs described in metadata

---

## state_getRuntimeVersion - Acala RPC Method

# state_getRuntimeVersion - Acala RPC Method

Returns the runtime version information for Acala, including the spec name, spec version, implementation version, and supported API versions.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_getRuntimeVersion` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Acala
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Acala in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/acala/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/acala/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Acala RPC Method

Returns the SCALE-encoded storage value for a given key on Acala. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_getStorage` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Acala
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Acala RPC Method
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/acala/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/acala/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call runtime APIs for computed state that is not directly in storage
- `state_subscribeStorage` -- Subscribe to storage changes in real time via WebSocket

---

## state_queryStorageAt - Acala RPC Method

Queries multiple storage keys at a specific block on Acala, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`state_queryStorageAt` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Acala
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Acala RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/acala/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/acala/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/acala/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/acala/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## system_chain - Acala RPC Method

Returns the chain name of the Acala network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`system_chain` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Network Verification** -- Confirm your application is connected to the correct Acala network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Acala"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://api-acala.n.dwellir.com/YOUR_API_KEY', 'Acala');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/acala/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/acala/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/acala/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/acala/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/acala/rpc_methods) -- List all available RPC methods

---

## system_health - Acala RPC Method

# system_health - Acala RPC Method

Returns the health status of the Acala node, including peer count, sync state, and whether the node expects to have peers.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`system_health` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Acala
- **Load Balancing** - Route requests only to healthy, fully synced nodes for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://api-acala.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-acala.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Acala:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Acala nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://api-acala.n.dwellir.com/YOUR_API_KEY')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/acala/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) - Get chain name
- `system_syncState` - Get detailed sync progress
- `system_peers` - Get detailed peer information

---

## system_name - Acala RPC Method

Returns the node implementation name on Acala. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`system_name` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Acala
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Acala node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Acala node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Acala node implementation: {name}')

# system_name - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
name = substrate.rpc_request('system_name', [])['result']
print(f'Acala node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Acala node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Acala nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/acala/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/acala/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/acala/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/acala/rpc_methods) -- List all available RPC methods

---

## system_properties - Acala RPC Method

Returns the chain-specific properties for Acala, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`system_properties` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Acala
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/acala/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/acala/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/acala/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/acala/rpc_methods) -- List all available RPC methods

---

## system_version - Acala RPC Method

Returns the node implementation version string on Acala. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Acala?** Build on Polkadot's DeFi and liquidity hub with aUSD stablecoin and liquid staking (LDOT) with $250M aUSD ecosystem fund, 150%+ LDOT TVL growth, micro gas fees payable in any token, and Coinbase Cloud partnership.

## When to Use This Method

`system_version` is essential for DeFi developers, stablecoin builders, and teams requiring cross-chain liquidity:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Acala
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for decentralized stablecoin (aUSD), liquid DOT staking (LDOT), and cross-chain AMM DEX
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-acala.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-acala.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Acala node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-acala.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Acala node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://api-acala.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Acala node version: {version}')

# system_version - Acala RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-acala.n.dwellir.com/YOUR_API_KEY')
version = substrate.rpc_request('system_version', [])['result']
print(f'Acala node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-acala.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Acala node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Acala nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/acala/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/acala/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/acala/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/acala/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/acala/rpc_methods) -- List all available RPC methods

---

## Agent Tooling

# Agent Tooling

Dwellir builds tools and infrastructure that AI coding agents can use directly. This page covers the CLI, agent skills, migration automation, and the documentation endpoints that make Dwellir's docs consumable by agents without HTML parsing.

## Dwellir CLI

The [Dwellir CLI](https://www.dwellir.com/docs/cli) gives agents full access to the Dwellir platform from the command line.
Every command supports both `--json` and `--toon` output.
In auto-detected non-interactive/agent environments, TOON is the default when no explicit output config exists.

Key capabilities for agents:

- **Endpoint discovery**: `dwellir endpoints list --json` returns the full catalog of 150+ blockchain endpoints with connection URLs, node types, and ecosystems.
- **API key management**: Create, rotate, and delete API keys programmatically with `dwellir keys create --name "agent-key" --json`.
- **Usage analytics**: Query request counts, RPS, costs, and error logs with structured TOON/JSON output.
- **Built-in docs**: `dwellir docs list` and `dwellir docs get <topic>` fetch Dwellir documentation as markdown directly in the terminal.

Agent-output benchmark details:

- Dwellir CLI benchmark run (Codex + Claude): [2026-03-03 JSON vs TOON benchmark](https://github.com/dwellir-public/cli/blob/main/docs/benchmarks/2026-03-03-agent-output-mode-json-vs-toon.md)
- TOON upstream benchmarks: [TOON benchmarks](https://github.com/toon-format/toon/tree/main/packages/toon#benchmarks)
- TOON caveats / when not to use: [When not to use TOON](https://github.com/toon-format/toon/tree/main/packages/toon#when-not-to-use-toon)

Install the CLI:

```bash
curl -fsSL https://raw.githubusercontent.com/dwellir-public/cli/main/scripts/install.sh | sh
```

See the full [CLI documentation](https://www.dwellir.com/docs/cli) for all commands and flags.

## Hyperliquid Agent Skill

The [Hyperliquid agent skill](https://github.com/dwellir-public/hyperliquid-skills) gives AI coding agents procedural knowledge for building on Hyperliquid through Dwellir's infrastructure. It follows the open [Agent Skills standard](https://skills.sh), making it portable across 40+ AI coding agents including Claude Code, Cursor, and Windsurf.

### What the skill provides

Once installed, the skill automatically activates when an agent encounters Hyperliquid-related tasks. It equips the agent with knowledge of:

- **HyperEVM JSON-RPC**: Query EVM state, deploy Solidity contracts (Chain ID: 999, gas token: HYPE)
- **Info API**: Access market data, prices, order books, candles, funding rates, user positions and balances
- **gRPC streaming**: Stream real-time L1 block data, fill executions, and order book snapshots
- **Order book WebSocket**: Real-time L2 and L4 depth data from edge servers in Singapore and Tokyo
- **Native API routing**: The skill teaches agents to route read operations through Dwellir endpoints and write operations (orders, transfers) through Hyperliquid's native API with EIP-712 signatures

### Install

```bash
npx skills add dwellir-public/hyperliquid-skills
```

This installs the skill into your project's `.claude/skills/` directory. No manual invocation is needed.

- [View on Skills.sh](https://skills.sh/dwellir-public/hyperliquid-skills/hyperliquid)
- [GitHub repository](https://github.com/dwellir-public/hyperliquid-skills)
- [Hyperliquid API documentation](https://www.dwellir.com/docs/hyperliquid)

## RPC Migration Prompt

The migration prompt automates switching a project's blockchain RPC endpoints from other providers to Dwellir. Copy it into your AI agent's context or use it as a system prompt. It walks agents through a structured 5-phase process: environment discovery, codebase scanning, compatibility matching, migration, and summary reporting.

View and copy the migration prompt

```text
Migrate this project's blockchain RPC endpoints to Dwellir. Follow the phases below in order. Use subagents and background processes to parallelize work wherever your tooling supports it.

## Phase 1 — Environment & Endpoint Discovery

1. Check whether the Dwellir CLI is installed by running `dwellir --version`. If the command is not found, suggest the user install it:
   curl -fsSL https://raw.githubusercontent.com/dwellir-public/cli/main/scripts/install.sh | sh
2. Obtain the full list of Dwellir-supported chains, networks, and node types (full vs archive). Try one of these approaches in order until one succeeds:
   a. **CLI** (preferred): Run `dwellir endpoints list` to get the complete endpoint catalog.
   b. **Documentation**: If the CLI is unavailable or the user declines to install it, fetch https://www.dwellir.com/docs.md or https://www.dwellir.com/networks.md for the supported endpoint list.
   c. **Dashboard export**: As a last resort, ask the user to go to dashboard.dwellir.com/endpoints and press the Export button (top-left) to export all endpoints as CSV, Markdown, or JSON, then share the file with you.
3. Check whether the project uses separate configurations per environment (production, staging, development, etc.). If it does, ask the user to provide a Dwellir API key for each environment. If there is only one environment, ask for a single key.

## Phase 2 — Codebase Discovery

Scan the entire codebase in parallel where possible:

1. Find every RPC endpoint URL (look for domains like infura.io, alchemy.com, quicknode.com, chainstack.com, ankr.com, blast.io, drpc.org, and any other known RPC providers, as well as raw IP/port patterns and chain-specific gateway URLs).
2. Identify each endpoint's chain, network, and authentication method (API key in URL path, header, query param, or none).
3. Determine whether the code requires an archive node or a full node for each endpoint (look for calls to historical state such as eth_getBalance at old block heights, debug_*/trace_* namespaces, or large block-range log filters).
4. Check if the codebase interacts with Hyperliquid. If it does, suggest that the user install Dwellir's Hyperliquid Skills: npx skills add https://github.com/dwellir-public/hyperliquid-skills

## Phase 3 — Compatibility Matching

For each discovered endpoint:

1. Compare the chain + network against the Dwellir endpoints list from Phase 1. Note that some providers (especially for EVM chains) use chain ID-based naming in their URLs rather than chain + network names — resolve any ambiguity by calling the endpoint's RPC method for chain ID (e.g., eth_chainId) and comparing the result against the chain ID returned by the corresponding Dwellir endpoint to confirm they serve the same network.
2. If the code requires an archive node and Dwellir only offers a full node for that chain, mark the endpoint as unsupported and do NOT migrate it.
3. For EVM chains, check whether the codebase depends on client-specific response shapes (e.g., Geth/Erigon trace formats vs Reth, differences in debug_traceTransaction output, or Parity-style trace_* responses). Use web search if needed to understand current client-level differences. Flag any potential incompatibilities.

## Phase 4 — Migration

1. Create a new branch (e.g., chore/migrate-to-dwellir) — NEVER commit directly to main.
2. For each supported endpoint, replace the provider URL with the equivalent Dwellir endpoint URL and update the authentication to use the correct Dwellir API key for each environment. Preserve the existing configuration pattern (env var, config file, etc.).
3. If any endpoints, chains, or networks in the codebase are NOT supported by Dwellir, do not touch them.

## Phase 5 — Summary

Present a clear summary with:

- Migrated: list of endpoints successfully switched to Dwellir (chain, network, full/archive).
- Flagged: any EVM client compatibility concerns the user should verify.
- Not supported: list of endpoints/chains/networks Dwellir does not currently support, along with whether each requires a full or archive node and the estimated monthly request volume if determinable from the code. Ask the user to reach out to support@dwellir.com or the team on https://t.me/dwellir with this list so Dwellir can evaluate adding support.

If there are any questions about supported RPC methods or Dwellir services, consult https://www.dwellir.com/docs/llms.txt and https://www.dwellir.com/docs.md for authoritative reference.

Commit the changes and ask the user whether you should push the branch to origin and open a pull request.
```

## Documentation for Agents

Dwellir's documentation is built with AI agent ergonomics in mind. Every page is available as clean markdown, and multiple discovery mechanisms help agents find the right content without HTML parsing.

### llms.txt

The site follows the [llmstxt.org](https://llmstxt.org) specification. Agents can fetch a structured index of all available content:

| Endpoint                                                            | Description                                                                     |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [`/llms.txt`](https://www.dwellir.com/llms.txt)                     | Site-wide index with links to all networks, blog posts, and documentation       |
| [`/docs/llms.txt`](https://www.dwellir.com/docs/llms.txt)           | Curated documentation index with network guides and key docs                    |
| [`/docs/llms-full.txt`](https://www.dwellir.com/docs/llms-full.txt) | Complete documentation concatenated into a single file for large-context agents |

### Markdown endpoints

Use the dedicated docs API endpoint or the existing `.md` shortcuts on networks and blog pages to get the content as plain markdown with `Content-Type: text/markdown` headers:

```
https://www.dwellir.com/api/docs/ethereum/markdown
https://www.dwellir.com/networks/ethereum.md
https://www.dwellir.com/blog/hyperliquid-rpc-providers.md
```

These endpoints strip all JSX/MDX components, rewrite internal links to absolute URLs, and include YAML frontmatter with title and description metadata.

### Smart 404 responses

When a documentation page is not found, the API returns fuzzy-matched suggestions instead of a generic error. Agents receive:

- A list of similar pages ranked by confidence score
- Direct links to the matching markdown URLs
- Fallback links to `/docs/llms.txt` and `/llms.txt` for broader discovery

Single exact matches (confidence 1.0) trigger an automatic redirect. This means minor typos or case mismatches resolve automatically.

### Content negotiation

All markdown endpoints set response headers that agents can use for content negotiation:

- `Content-Type: text/markdown; charset=utf-8`
- `Link` header with `rel="canonical"` (HTML version) and `rel="alternate"` (markdown version)
- `X-Robots-Tag: noindex, follow` to prevent search engine indexing of markdown versions

### Index endpoints

Two index endpoints provide structured lists of all available content:

- [`/docs.md`](https://www.dwellir.com/docs.md) - All documentation pages with titles, descriptions, and markdown links
- [`/networks.md`](https://www.dwellir.com/networks.md) - All supported networks with availability, regions, and pricing summaries

## Next steps

- [Install the CLI](https://www.dwellir.com/docs/cli/installation) and authenticate with `dwellir auth login`
- [Install the Hyperliquid skill](https://github.com/dwellir-public/hyperliquid-skills) for AI-assisted Hyperliquid development
- Fetch [`/llms.txt`](https://www.dwellir.com/llms.txt) to discover all available documentation
- [Contact the Dwellir team](mailto:support@dwellir.com) for dedicated node access or custom integrations

---

## Aptos Network Documentation

# Aptos Network Documentation

## Why Build on Aptos

- 160K+ TPS via Block-STM parallel execution
- Sub-second finality (\~250 ms blocks) with AptosBFT v4
- Resource-oriented Move language with formal verification
- Native sponsored and multi-agent transactions
- Object model for powerful composition
- Evolving token standards (Fungible Asset and Digital Asset)

## Quick Start

cURL
TypeScript SDK (@aptos-labs/ts-sdk)
Python SDK (aptos-sdk)
Rust SDK

```bash
curl -X GET https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1 \
  -H "Accept: application/json"
```

```ts
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  network: Network.MAINNET,
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1",
});
const aptos = new Aptos(config);

const info = await aptos.getLedgerInfo();
console.log(info.chain_id, info.ledger_version);
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
info = client.get_ledger_info()
print(info["chain_id"], info["ledger_version"])  # 1 APT = 100,000,000 Octas
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let info = client.get_ledger_information().await?;
println!("{} {}", info.inner().chain_id, info.inner().ledger_version);
```

## Network Information

| Parameter    | Value       | Details                   |
| ------------ | ----------- | ------------------------- |
| Chain ID     | 1           | Mainnet                   |
| Native Token | APT         | 1 APT = 100,000,000 Octas |
| Consensus    | AptosBFT v4 | \~250 ms blocks           |
| Execution    | Block-STM   | Parallel transactions     |

## REST API Reference

- Base path: `/v1`
- Auth: Add your key in the URL path: `https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1`
- JSON responses, snake\_case fields, bcs-encoded payloads for transactions

## GraphQL API Reference

- Indexer GraphQL endpoint: `https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql`
- Advanced queries for transactions, tokens, ANS, aggregations
- Real-time subscription support (see Streaming for gRPC)

See the GraphQL section for examples and schema notes.

## Transaction Stream Service

- gRPC-based real-time transaction stream
- Historical replay from genesis, checkpointing, custom processors
- Early access available. Email `support@dwellir.com`

## Move Development

- Entry functions (transactions) vs view functions (read-only)
- Modules, resources, and abilities (key, store, drop, copy)
- Testing with Move unit tests and Move Prover

## Object Model

- Globally addressable objects enable safe composition
- Reference types: `ConstructorRef`, `MutatorRef`, `DeleteRef`
- Ownership hierarchies and type-safe conversions

## Token Standards

- Legacy: `0x1::coin`, `0x3::token`
- Current: Fungible Asset (FA) and Digital Asset (DA)
- Migration patterns and compatibility tips

## Unique Aptos Features

- Sponsored transactions (fee payer)
- Multi-agent transactions (multiple signers)
- Key rotation (proven/unproven)
- Aggregator V2 counters
- Orderless transactions

## Code Examples

Find end-to-end examples for transfers, NFTs, module publish, and token ops in the respective sections.

## Performance Optimization

- Parallel-friendly design patterns (minimize write conflicts)
- Gas optimization via simulation and views
- Aggregators for scalable counters
- Batching reads and writes when appropriate

## Troubleshooting

- Common REST errors, transaction failures, module verification
- Gas estimation pitfalls and how to resolve them

## Resources & Tools

- Official SDKs (TS, Python, Rust)
- Explorers, faucets, testing tools, community links

---

## accounts_get

# accounts_get

## Overview

Returns core account information (sequence number and authentication key) for a given address.

## Endpoint

`GET /v1/accounts/{address}`

## Aptos-Specific Notes

- Accounts are resources; sequence numbers are required for building transactions.
- Addresses are 0x-prefixed hex strings; leading zeros are accepted.

## Request

### Path Parameters

| Name    | Type   | Required | Description                   |
| ------- | ------ | -------- | ----------------------------- |
| address | string | Yes      | Account address (e.g., `0x1`) |

### Query Parameters

None.

### Request Body

None.

## Response

### Success Response (200)

```
{
  "sequence_number": "string",
  "authentication_key": "string"
}
```

### Error Responses

| Status | Error Code          | Description            |
| ------ | ------------------- | ---------------------- |
| 400    | invalid\_input      | Invalid address format |
| 404    | account\_not\_found | Account doesn't exist  |
| 500    | internal\_error     | Server error           |

## Code Examples

cURL
Python
TypeScript
Rust SDK

```
curl -X GET https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1 \
  -H "Accept: application/json"
```

SDK

```
from aptos_sdk.client import RestClient
client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
account = client.account("0x1")
```

```
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
const aptos = new Aptos(new AptosConfig({ network: Network.MAINNET, fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1" }));
const account = await aptos.getAccountInfo({ accountAddress: "0x1" });
```

```
use aptos_sdk::rest_client::Client;
let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let account = client.get_account("0x1").await?;
```

## Common Use Cases

- Checking account existence before transactions
- Retrieving authentication keys for verification
- Getting sequence numbers for transaction building

## Related Endpoints

- accounts\_resources - Get account resources
- accounts\_modules - Get account modules

---

## accounts_module

# accounts_module

## Overview

Fetch a specific Move module published under an account by module name.

## Endpoint

`GET /v1/accounts/{address}/module/{module_name}`

## Request

### Path Parameters

| Name         | Type   | Required | Description                     |
| ------------ | ------ | -------- | ------------------------------- |
| address      | string | Yes      | Account address                 |
| module\_name | string | Yes      | Module name (no address prefix) |

## Code Examples

cURL
Python
TypeScript
Rust

```
curl -X GET https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/module/aptos_account \
  -H "Accept: application/json"
```

```
module = client.account_module("0x1", "aptos_account")
```

```
const m = await aptos.getAccountModule({ accountAddress: "0x1", moduleName: "aptos_account" });
```

```
let m = client.get_account_module("0x1", "aptos_account").await?;
```

## Response

### Success Response (200)

Returns a Move module object containing:

```json
{
  "bytecode": "0xa11ceb0b060000000...",
  "abi": {
    "address": "0x1",
    "name": "aptos_account",
    "friends": [],
    "exposed_functions": [...],
    "structs": [...]
  }
}
```

The ABI includes complete type information, function signatures, and struct definitions for the module.

### Error Responses

| Status | Error Code          | Description                           |
| ------ | ------------------- | ------------------------------------- |
| 400    | invalid\_input      | Invalid address or module name format |
| 404    | module\_not\_found  | Module doesn't exist at this address  |
| 404    | account\_not\_found | Account doesn't exist                 |

## Use Cases

This endpoint is essential for several blockchain development workflows:

1. **Smart Contract Verification**: Verify the bytecode and ABI of deployed modules to ensure they match expected implementations before interacting with them.

2. **ABI Discovery**: Retrieve complete type information and function signatures for integration with wallets, dapps, or SDKs without requiring the original Move source code.

3. **Upgrade Auditing**: Compare module versions across different ledger versions to track changes and validate upgrade compatibility rules.

4. **Development Tools**: IDE plugins and debugging tools use this endpoint to provide autocomplete, type checking, and inline documentation for on-chain modules.

5. **Security Analysis**: Security researchers analyze deployed module bytecode to identify potential vulnerabilities or verify formal verification properties.

6. **Cross-Contract Integration**: Before calling functions in another module, applications can inspect the ABI to ensure compatibility and understand input/output types.

## Best Practices

**Module Name Format**: The module name parameter should not include the address prefix. Use `aptos_account` not `0x1::aptos_account`.

**Caching Strategy**: Module bytecode rarely changes. Implement client-side caching with ledger version as the cache key to minimize API calls. Only refetch when you detect a module upgrade event.

**ABI Type Resolution**: When parsing generic types in the ABI, ensure your client handles nested generics like `CoinStore<AptosCoin>` correctly. Use the full type path for disambiguation.

**Version Pinning**: For production applications, consider querying modules at specific ledger versions rather than always using the latest state. This provides deterministic behavior even during network upgrades.

**Error Handling**: A 404 response could mean either the account doesn't exist or the module doesn't exist at that account. Check the error message to distinguish these cases.

## Performance Considerations

Module queries are relatively lightweight operations. The response size depends on module complexity - simple modules return a few KB, while complex framework modules may return 100KB+ of bytecode and ABI data. If you only need specific information from the ABI, consider parsing it client-side and caching the relevant portions.

The REST API serves modules from the node's storage layer with minimal processing overhead. Response times are typically under 100ms for hot data.

---

## accounts_modules

# accounts_modules

## Overview

Returns all Move modules published under an account. Each module includes its compiled bytecode and the ABI (Application Binary Interface) describing the module's structs, functions, and type parameters. This endpoint is fundamental for on-chain contract discovery, ABI-driven integrations, and upgrade auditing.

> **Operational note:** On Dwellir shared Aptos fullnodes, this route depends on indexer-backed helpers. If the upstream indexer reader is unavailable, requests can fail with `internal_error`. For production discovery workflows, keep a GraphQL fallback ready for large crawls and historical backfills.

## Endpoint

`GET /v1/accounts/{address}/modules`

## Aptos-Specific Notes

- Move modules are published to the address of the deploying account and become part of that account's on-chain state.
- Use this endpoint for ABI discovery, upgrade auditing, and verifying deployed contract interfaces.
- The `0x1` address contains all Aptos framework modules (coin, account, staking, governance, etc.).
- Module bytecode is the compiled Move output; decompilation is possible but not lossless.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# List all modules under the Aptos framework
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/modules" \
      -H "Accept: application/json"

    # List modules at a specific historical version
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/modules?ledger_version=50000000" \
      -H "Accept: application/json"
```

## Response Fields

- `bytecode` (`string, required`): Hex-encoded compiled Move bytecode
- `abi.name` (`string, required`): Module name
- `abi.address` (`string, required`): Address where the module is published
- `abi.exposed_functions` (`array, required`): Public and entry functions callable externally
- `abi.structs` (`array, required`): Struct definitions including fields and abilities
- `abi.friends` (`array, required`): Other modules granted friend access

## Successful Response

```json
[
  {
    "bytecode": "0xa11ceb0b...",
    "abi": {
      "address": "0x1",
      "name": "coin",
      "friends": ["0x1::aptos_coin"],
      "exposed_functions": [
        {
          "name": "balance",
          "visibility": "public",
          "is_entry": false,
          "is_view": true,
          "generic_type_params": [{ "constraints": [] }],
          "params": ["address"],
          "return": ["u64"]
        },
        {
          "name": "transfer",
          "visibility": "public",
          "is_entry": true,
          "is_view": false,
          "generic_type_params": [{ "constraints": [] }],
          "params": ["&signer", "address", "u64"],
          "return": []
        }
      ],
      "structs": [
        {
          "name": "CoinStore",
          "is_native": false,
          "abilities": ["key"],
          "generic_type_params": [{ "constraints": [] }],
          "fields": [
            { "name": "coin", "type": "0x1::coin::Coin<T0>" },
            { "name": "frozen", "type": "bool" }
          ]
        }
      ]
    }
  }
]
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid address format

### Error 2

- Code: `account_not_found`
- Description: Account does not exist

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# List all modules under the Aptos framework
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/modules" \
  -H "Accept: application/json"

# List modules at a specific historical version
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/modules?ledger_version=50000000" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Get all modules for an account
modules = client.account_modules("0x1")

# List module names
for mod in modules:
    print(f"Module: {mod['abi']['name']}")

# Extract entry functions from a specific module
for mod in modules:
    if mod["abi"]["name"] == "coin":
        for fn in mod["abi"]["exposed_functions"]:
            if fn["is_entry"]:
                print(f"  Entry: {fn['name']}({', '.join(fn['params'])})")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Get all modules published by an account
const modules = await aptos.getAccountModules({ accountAddress: "0x1" });
console.log(`Found ${modules.length} modules`);

// Find view functions for building read-only queries
for (const mod of modules) {
  for (const fn of mod.abi?.exposed_functions ?? []) {
    if (fn.is_view) {
      console.log(`View: ${mod.abi?.name}::${fn.name}`);
    }
  }
}
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let modules = client.get_account_modules("0x1").await?;
for module in modules.inner() {
    println!("Module: {}", module.abi.as_ref().unwrap().name);
}
```

## Use Cases

1. **ABI Discovery**: Dynamically discover the interface of any deployed contract without prior knowledge. Build generic tools that can interact with any Move module by reading its ABI at runtime.

2. **Upgrade Auditing**: Compare modules at different `ledger_version` values to detect what changed in a contract upgrade, including new functions, modified parameters, or structural changes.

3. **Contract Verification**: Verify that deployed bytecode matches expected source code by compiling locally and comparing bytecode hashes.

4. **SDK Generation**: Auto-generate type-safe client libraries from on-chain ABIs, ensuring your integration stays in sync with the deployed contract.

5. **Security Analysis**: Inspect exposed functions, their visibility levels, and parameter types to assess the attack surface of a smart contract before interacting with it.

6. **Protocol Integration**: Before integrating with a DeFi protocol or NFT marketplace, fetch its modules to understand available entry points, required arguments, and return types.

## Best Practices

**Large Accounts**: Framework addresses like `0x1` publish dozens of modules. Use pagination (`limit` and `start`) to control response size, or fetch a single module by name using the `accounts_module` endpoint. If the shared indexer path is unavailable, fall back to GraphQL for complete catalog exports.

**Bytecode Size**: Bytecode strings can be large (10KB-500KB per module). If you only need the ABI, parse the `abi` field and discard `bytecode` to reduce memory usage.

**Historical Versions**: When auditing upgrades, query at the version before and after the upgrade transaction to see exactly what changed. Find the upgrade transaction version from the account's transaction history.

**Type Parameters**: Generic type parameters in functions and structs use positional references (`T0`, `T1`). Map these to the `generic_type_params` array for constraint information.

**Friend Modules**: The `friends` list shows which other modules have privileged access. This is important for security audits -- friend functions can bypass normal visibility restrictions.

**Caching**: Module bytecode changes only when the module is upgraded. Cache aggressively and invalidate when you detect an upgrade transaction for the account.

## Performance Considerations

Fetching all modules for an account can be slow for addresses with many deployments. The `0x1` framework address has 50+ modules, resulting in responses of 1-5MB and response times of 200-500ms.

For single-module lookups, use `GET /v1/accounts/{address}/module/{module_name}` instead, which returns in 50-100ms with a much smaller payload.

Bytecode dominates response size. If you only need the ABI (function signatures, struct definitions), consider fetching the full response once and caching the ABIs separately.

## Related Endpoints

- `/v1/accounts/{address}/module/{module_name}` - Get a single module by name
- `/v1/accounts/{address}/resources` - Get account resources (runtime state of modules)
- `/v1/accounts/{address}` - Get account info including authentication key
- `/v1/view` - Call view functions discovered through module ABIs

---

## accounts_resource

# accounts_resource

## Overview

Fetch a specific resource stored under an account by its full Move type string. Resources are the primary state-storage mechanism in Move -- every token balance, NFT collection, staking position, and application-specific datum is stored as a typed resource under an account address. This endpoint provides direct, type-safe access to individual resources.

## Endpoint

`GET /v1/accounts/{address}/resource/{resource_type}`

## Aptos-Specific Notes

- Resource types follow the format `address::module::StructName<TypeArgs>`.
- Generic types must include full type parameters, such as `0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>`.
- The resource type string must be URL-encoded when it contains special characters like `<`, `>`, or `::`.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# Get APT balance for an account that actually stores AptosCoin
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/YOUR_ACCOUNT_ADDRESS/resource/0x1::coin::CoinStore%3C0x1::aptos_coin::AptosCoin%3E" \
      -H "Accept: application/json"

    # Get account metadata
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resource/0x1::account::Account" \
      -H "Accept: application/json"

    # Get DKG state (no URL encoding needed for simple types)
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resource/0x1::dkg::DKGState" \
      -H "Accept: application/json"
```

## Response Fields

- `type` (`string, required`): The fully qualified Move type of the resource
- `data` (`object, required`): The resource's fields serialized as JSON
- `data.coin.value` (`string, required`): For CoinStore, the balance in the smallest unit (octas for APT)
- `data.frozen` (`boolean, required`): For CoinStore, whether transfers are frozen
- `data.deposit_events` (`object, required`): Event handle for tracking deposits
- `data.withdraw_events` (`object, required`): Event handle for tracking withdrawals

## Successful Response

```json
{
  "type": "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
  "data": {
    "coin": {
      "value": "1500000000"
    },
    "deposit_events": {
      "counter": "42",
      "guid": {
        "id": {
          "addr": "0x1",
          "creation_num": "2"
        }
      }
    },
    "withdraw_events": {
      "counter": "15",
      "guid": {
        "id": {
          "addr": "0x1",
          "creation_num": "3"
        }
      }
    },
    "frozen": false
  }
}
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid address or malformed resource type string

### Error 2

- Code: `resource_not_found`
- Description: Resource type does not exist under this account

### Error 3

- Code: `account_not_found`
- Description: Account does not exist

## Common Resource Types

| Resource Type                                      | Description                                            |
| -------------------------------------------------- | ------------------------------------------------------ |
| `0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>` | APT token balance                                      |
| `0x1::account::Account`                            | Account metadata (sequence number, authentication key) |
| `0x1::staking_contract::StakingGroupContainer`     | Staking positions                                      |
| `0x1::token::TokenStore`                           | Legacy token (NFT) storage                             |
| `0x4::collection::Collection`                      | Digital Asset (NFT) collection                         |
| `0x1::coin::CoinInfo<0x1::aptos_coin::AptosCoin>`  | APT coin metadata (decimals, name, symbol)             |
| `0x1::stake::StakePool`                            | Validator stake pool info                              |
| `0x1::dkg::DKGState`                               | Distributed key generation state                       |

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Get APT balance for an account that actually stores AptosCoin
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/YOUR_ACCOUNT_ADDRESS/resource/0x1::coin::CoinStore%3C0x1::aptos_coin::AptosCoin%3E" \
  -H "Accept: application/json"

# Get account metadata
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resource/0x1::account::Account" \
  -H "Accept: application/json"

# Get DKG state (no URL encoding needed for simple types)
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resource/0x1::dkg::DKGState" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Get APT balance
resource = client.account_resource(
    "YOUR_ACCOUNT_ADDRESS",
    "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"
)
balance_octas = int(resource["data"]["coin"]["value"])
balance_apt = balance_octas / 100_000_000
print(f"Balance: {balance_apt} APT")

# Get account sequence number
account = client.account_resource("0x1", "0x1::account::Account")
print(f"Sequence number: {account['data']['sequence_number']}")

# Get resource at a historical version
import requests
response = requests.get(
    "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resource/0x1::dkg::DKGState",
    params={"ledger_version": "50000000"},
    headers={"Accept": "application/json"}
)
historical = response.json()
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Get APT balance
const resource = await aptos.getAccountResource({
  accountAddress: "YOUR_ACCOUNT_ADDRESS",
  resourceType: "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
});
const balanceOctas = BigInt(resource.coin.value);
console.log(`Balance: ${Number(balanceOctas) / 1e8} APT`);

// Get account info
const account = await aptos.getAccountResource({
  accountAddress: "0x1",
  resourceType: "0x1::account::Account",
});
console.log(`Sequence: ${account.sequence_number}`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

// Get a specific resource
let resource = client
    .get_account_resource("0x1", "0x1::dkg::DKGState")
    .await?;
println!("{:?}", resource.inner());

// Get APT coin store
let coin_store = client
    .get_account_resource("YOUR_ACCOUNT_ADDRESS", "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>")
    .await?;
```

## Use Cases

1. **Balance Checking**: Read `CoinStore` resources to check token balances for any fungible asset (APT, USDC, custom tokens) before initiating transfers or swaps.

2. **State Inspection**: Read application-specific resources to check contract state without executing transactions -- useful for displaying DeFi positions, staking info, or governance data in UIs.

3. **Historical State Queries**: Use the `ledger_version` parameter to read resource state at any historical point, enabling time-series analysis and state reconstruction.

4. **Event Handle Discovery**: Extract event handle creation numbers from resource fields to set up event monitoring via the events endpoints.

5. **Pre-Transaction Validation**: Check resource existence and values before building transactions. For example, verify a CoinStore exists before attempting a transfer to avoid unnecessary gas costs.

6. **Protocol Monitoring**: Monitor key protocol resources (treasury balances, governance parameters, oracle prices) for dashboards and alerting systems.

## Best Practices

**URL Encoding**: Resource types containing `<` and `>` (generics) must be URL-encoded. Use `%3C` for `<` and `%3E` for `>`. Most HTTP libraries handle this automatically.

**Type Precision**: The resource type must match exactly as stored on-chain, including the full address prefix. `coin::CoinStore` will not work; use `0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>`.

**Version Pinning**: When reading multiple related resources that must be consistent (such as balance and allowance), pass the same `ledger_version` to all requests to get an atomic snapshot.

**404 Handling**: A 404 for a CoinStore means the account has never held that token (the store has not been created), not that the balance is zero. Distinguish between "not registered" and "zero balance" in your application logic.

**Large Resources**: Some resources (like tables or large vectors) can be very large. If you only need a specific field, consider using the `view` endpoint to call a view function that returns just the data you need.

**Numeric Values**: All large numbers (balances, timestamps, IDs) are returned as strings to avoid JavaScript precision loss. Parse with BigInt or appropriate large-number libraries.

## Performance Considerations

Single resource lookups are highly optimized, completing in 30-80ms. The response size depends on the resource structure: simple resources like `Account` return under 1KB, while complex resources with large vectors or nested structs can be 10-100KB.

For applications that need multiple resources from the same account, it may be more efficient to use `GET /v1/accounts/{address}/resources` to fetch all resources in a single request, then filter client-side. However, for accounts with many resources (like `0x1`), targeted single-resource queries are faster.

Historical queries (with `ledger_version`) may be slightly slower than current-state queries if the node needs to read from archived storage.

## Related Endpoints

- `/v1/accounts/{address}/resources` - Get all resources under an account
- `/v1/accounts/{address}/module/{module_name}` - Get module ABI to discover resource types
- `/v1/view` - Call view functions for computed resource data
- `/v1/tables/{handle}/item` - Read individual table entries within resources

---

## accounts_resources

# accounts_resources

## Overview

Lists all Move resources stored under an account. Useful to discover balances, capabilities, and custom module data.

## Endpoint

`GET /v1/accounts/{address}/resources`

## Aptos-Specific Notes

- Resource types follow `address::module::Struct<...>`.
- Large accounts may have many resources; use pagination parameters if present.

## Request

### Path Parameters

| Name    | Type   | Required | Description                   |
| ------- | ------ | -------- | ----------------------------- |
| address | string | Yes      | Account address (e.g., `0x1`) |

### Query Parameters

| Name            | Type    | Required | Description                       |
| --------------- | ------- | -------- | --------------------------------- |
| ledger\_version | string  | No       | Read at a historical version      |
| limit           | integer | No       | Max number of resources to return |
| start           | string  | No       | Cursor for pagination             |

### Request Body

None.

## Response

### Success Response (200)

```
[
  {
    "type": "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
    "data": { "coin": { "value": "123" } }
  }
]
```

### Error Responses

| Status | Error Code          | Description            |
| ------ | ------------------- | ---------------------- |
| 400    | invalid\_input      | Invalid address format |
| 404    | account\_not\_found | Account doesn't exist  |

## Code Examples

cURL
Python
TypeScript
Rust SDK

```
curl -X GET https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/resources?limit=100 \
  -H "Accept: application/json" \
  -H "Accept: application/json"
```

SDK

```
from aptos_sdk.client import RestClient
client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
resources = client.account_resources("0x1")
```

```
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
const aptos = new Aptos(new AptosConfig({ network: Network.MAINNET, fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1" }));
const resources = await aptos.getAccountResources({ accountAddress: "0x1" });
```

```
use aptos_sdk::rest_client::Client;
let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let resources = client.get_account_resources_bcs("0x1").await?;
```

## Common Use Cases

- Token balances (CoinStore, FA)
- Capability checks
- Reading application state

## Related Endpoints

- accounts\_resource - Get a single resource type

---

## accounts_transactions

# accounts_transactions

## Overview

Returns committed transactions where the account is the sender. This is the canonical REST endpoint for building account activity feeds, transaction history pages, reconciliation jobs, and "recent actions" views keyed to one Aptos address.

> **Operational note:** On Dwellir shared Aptos fullnodes, this route depends on indexer-backed history readers. If the node reports `Indexer reader is None` or another `internal_error`, switch to [GraphQL](https://www.dwellir.com/docs/aptos/graphql/overview) for indexed account history.

## Endpoint

`GET /v1/accounts/{address}/transactions`

Use this endpoint when you want all transactions submitted by one account and you do not need cross-account joins. If you need a specific transaction by hash, use [`transactions_by_hash`](https://www.dwellir.com/docs/aptos/transactions_by_hash). If you need indexed analytics or richer joins, use [GraphQL](https://www.dwellir.com/docs/aptos/graphql/overview).

## Request Parameters

- `address` (`string, required`): Path parameter: Aptos account address for the sender whose committed transactions you want to page through
- `start` (`string, optional`): Query parameter: Starting cursor for pagination
- `limit` (`integer, optional`): Query parameter: Maximum number of transactions to return in one page

## Request Example

```bash
# Get the latest 25 transactions for an account
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/transactions?limit=25" \
      -H "Accept: application/json"

    # Paginate from a specific sequence number
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/transactions?start=100&limit=50" \
      -H "Accept: application/json"
```

## Response Fields

- `result` (`OBJECT, required`): Each item in the response is a committed transaction object. For `user_transaction` entries, the most commonly-used fields are: ```json [ { "version": "238497201", "hash": "0x5bb5d8f4...", "type": "user_transaction", "sender": "0x1", "sequence_number": "352", "max_gas_amount": "2000", "gas_used": "8", "success": true, "vm_status": "Executed successfully", "timestamp": "1736443872382624" } ] ``` For account history pages, `version`, `hash`, `sequence_number`, `success`, and `timestamp` are usually enough to render a first-pass list. Pull the full transaction by hash only when the user opens a detail view.

## Successful Response

```json
[
  {
    "version": "238497201",
    "hash": "0x5bb5d8f4...",
    "type": "user_transaction",
    "sender": "0x1",
    "sequence_number": "352",
    "max_gas_amount": "2000",
    "gas_used": "8",
    "success": true,
    "vm_status": "Executed successfully",
    "timestamp": "1736443872382624"
  }
]
```

## Response Shape

Each item in the response is a committed transaction object. For `user_transaction` entries, the most commonly-used fields are:

```json
[
  {
    "version": "238497201",
    "hash": "0x5bb5d8f4...",
    "type": "user_transaction",
    "sender": "0x1",
    "sequence_number": "352",
    "max_gas_amount": "2000",
    "gas_used": "8",
    "success": true,
    "vm_status": "Executed successfully",
    "timestamp": "1736443872382624"
  }
]
```

For account history pages, `version`, `hash`, `sequence_number`, `success`, and `timestamp` are usually enough to render a first-pass list. Pull the full transaction by hash only when the user opens a detail view.

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Get the latest 25 transactions for an account
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/transactions?limit=25" \
  -H "Accept: application/json"

# Paginate from a specific sequence number
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/transactions?start=100&limit=50" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Fetch recent transactions
txns = client.get_account_transactions("0x1", params={"limit": 25})
for txn in txns:
    print(f"Seq {txn['sequence_number']}: {txn['payload']['function']} - {txn['vm_status']}")

# Paginate through all account transactions
all_txns = []
start = None
while True:
    params = {"limit": 100}
    if start is not None:
        params["start"] = start
    batch = client.get_account_transactions("0x1", params=params)
    if not batch:
        break
    all_txns.extend(batch)
    start = str(int(batch[-1]["sequence_number"]) + 1)
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Fetch recent transactions
const txns = await aptos.getAccountTransactions({
  accountAddress: "0x1",
  options: { offset: 0, limit: 25 }
});
console.log(`Found ${txns.length} transactions`);

// Process each transaction
for (const txn of txns) {
  console.log(`Version ${txn.version}: ${txn.success ? "OK" : "FAILED"}`);
}
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let txns = client.get_account_transactions("0x1", None, Some(25)).await?;
for txn in txns.inner() {
    println!("Seq {}: {}", txn.sequence_number, txn.vm_status);
}
```

## Pagination Guide

This endpoint paginates by sequence number using `start` and `limit`:

1. **First page**: Omit `start` to begin at the account's earliest committed transactions.
2. **Next page**: Use `last_sequence_number + 1` as the next `start` value.
3. **End detection**: An empty array or a short page means you have reached the end of the account's committed sender history.

Sequence numbers are per-sender and increment by 1 for every committed transaction from that account.

## Practical Guidance

- This endpoint is sender-scoped. It does not show incoming transfers or third-party activity that merely touched the account.
- For first-pass account history, render `version`, `hash`, `sequence_number`, `success`, and `timestamp`, then fetch a single transaction detail only when the user drills in.
- For richer joins or cross-account analytics, use Aptos GraphQL instead of repeatedly walking sender histories.

**Sequence Number Gaps**: If the account has failed transactions (which still consume gas and increment the sequence number), they appear in the results with `success: false`. Account for these when building activity feeds.

**Limit Selection**: Use `limit=100` for bulk retrieval to minimize API calls. Use smaller limits (10-25) for UI pagination where you display a few items per page.

**Caching Strategy**: Older transactions are immutable. Cache pages of historical transactions permanently and only re-fetch the latest page to check for new transactions.

**Address Normalization**: Ensure addresses are fully expanded to 66 characters (0x + 64 hex digits) with leading zeros. Short-form addresses like `0x1` are accepted but normalizing avoids inconsistencies.

## Performance Considerations

Query response time scales with the `limit` parameter. Fetching 25 transactions typically completes in 50-100ms, while 100 transactions may take 100-200ms depending on transaction complexity and payload sizes.

Accounts with thousands of transactions can be paginated efficiently because sequence numbers provide a natural index. The database uses the (address, sequence\_number) pair as a primary key, making lookups O(1) for any page.

For accounts with high transaction volume, consider storing the last-seen sequence number in your application and only fetching new transactions since that point.

## Related Endpoints

- `/v1/accounts/{address}` - Get account info including current sequence number
- `/v1/accounts/{address}/resources` - Get account resources (balances, state)
- `/v1/accounts/{address}/events/{creation_number}` - Get events for an account
- `/v1/transactions/by_hash/{hash}` - Look up a specific transaction by hash

## What to Watch For

- This endpoint is sender-scoped. Incoming transfers are not guaranteed to appear unless the queried account actually submitted the transaction.
- Results are committed ledger history, so this is the right source for confirmed account activity rather than pending transaction tracking.
- Keep `limit` small while developing. System accounts such as `0x1` can return large histories quickly.
- Use the cursor fields your client already stores for pagination instead of re-reading from the beginning on every sync.
- Treat GraphQL as the fallback for bulk history jobs if the shared node's indexer-backed reader is unavailable.

## Practical Patterns

### Activity Feed

Use `limit` plus your last stored cursor to incrementally extend an account history view without re-fetching older pages.

### Retry and Reconciliation

If your application stores transaction hashes after submission, compare the stored sender and sequence number against this endpoint to verify that account history is complete.

### Compliance and Auditing

Because the response is committed ledger data, it is suitable for downstream audit logs and reconciliation jobs that should not include mempool noise.

## Related Endpoints

- [transactions\_by\_hash](https://www.dwellir.com/docs/aptos/transactions_by_hash) for one known transaction
- [blocks\_by\_height](https://www.dwellir.com/docs/aptos/blocks_by_height) for block-scoped reads
- [GraphQL Overview](https://www.dwellir.com/docs/aptos/graphql/overview) for indexed history and analytics

---

## aggregations

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# aggregations

GraphQL aggregations enable powerful analytical queries on Aptos blockchain data, allowing you to compute statistics, counts, sums, and other aggregate functions across large datasets. These queries are essential for building analytics dashboards, generating reports, and understanding on-chain activity patterns without processing individual records.

## Overview

The Aptos GraphQL indexer provides aggregate functions that operate on filtered datasets, enabling you to answer questions like "How many transactions occurred today?", "What's the total trading volume?", or "Who are the top gas consumers?" These aggregations run efficiently on indexed data, providing fast results even for complex queries spanning millions of records.

## Common Aggregate Functions

The GraphQL API supports standard aggregate operations:

**count**: Total number of records matching criteria
**sum**: Sum of numeric field values
**avg**: Average of numeric values
**max**: Maximum value in dataset
**min**: Minimum value in dataset
**stddev**: Standard deviation of values
**variance**: Variance of values

## Example Queries

### Transaction Volume Analysis

```graphql
query TransactionMetrics($startTime: timestamp!, $endTime: timestamp!) {
  user_transactions_aggregate(
    where: {
      timestamp: { _gte: $startTime, _lte: $endTime }
    }
  ) {
    aggregate {
      count
      sum { gas_used }
      avg { gas_used }
      max { gas_used }
      min { gas_used }
    }
  }
}
```

### Top Gas Consumers

```graphql
query TopGasUsers($limit: Int!) {
  user_transactions_aggregate {
    aggregate { count }
  }
  user_transactions(
    order_by: { gas_used: desc },
    limit: $limit
  ) {
    sender
    gas_used
    version
    timestamp
  }
}
```

### Daily Active Users

```graphql
query DailyActiveUsers($date: date!) {
  user_transactions_aggregate(
    distinct_on: sender,
    where: {
      timestamp: {
        _gte: $date,
        _lt: "${date + 1 day}"
      }
    }
  ) {
    aggregate {
      count(distinct: true, columns: sender)
    }
  }
}
```

### Token Transfer Statistics

```graphql
query TokenTransferStats($token_type: String!) {
  coin_activities_aggregate(
    where: {
      coin_type: { _eq: $token_type },
      activity_type: { _eq: "0x1::coin::WithdrawEvent" }
    }
  ) {
    aggregate {
      count
      sum { amount }
      avg { amount }
    }
  }
}
```

### NFT Collection Analytics

```graphql
query CollectionMetrics($collection_id: String!) {
  current_token_ownerships_v2_aggregate(
    where: {
      current_token_data: {
        collection_id: { _eq: $collection_id }
      }
    }
  ) {
    aggregate {
      count
    }
  }

  token_activities_v2_aggregate(
    where: {
      token_data_id: { _like: "${collection_id}%" },
      type: { _eq: "0x3::token::MintTokenEvent" }
    }
  ) {
    aggregate {
      count
    }
  }
}
```

## Real-World Use Cases

1. **Protocol Analytics**: Track total value locked, transaction volumes, user growth, and other key metrics for DeFi protocols and dApps.

2. **User Behavior Analysis**: Understand user engagement patterns, identify power users, and analyze transaction frequency distributions.

3. **Gas Optimization Research**: Analyze gas consumption patterns to identify optimization opportunities and compare efficiency across different contract designs.

4. **Market Intelligence**: Aggregate trading volumes, price movements, and liquidity metrics for tokens and NFT collections.

5. **Network Health Monitoring**: Track transaction success rates, average confirmation times, and network utilization over time.

6. **Revenue Reporting**: Calculate total fees collected, transaction counts by type, and other financial metrics for business reporting.

## Best Practices

**Use Appropriate Filters**: Apply WHERE clauses to reduce dataset size before aggregation for better performance.

**Leverage Indexed Fields**: Aggregations on indexed fields (addresses, timestamps, types) perform significantly faster.

**Batch Time-Series Queries**: For dashboards displaying multiple time periods, batch queries together to reduce API calls.

**Cache Results**: Aggregate statistics change slowly - implement appropriate caching strategies to reduce load.

**Pagination for Details**: When showing aggregate summaries plus details, paginate the detail results appropriately.

**Use Variables**: Parameterize queries with GraphQL variables for reusable, type-safe queries.

## Performance Considerations

- Aggregations on large unfiltered tables can be slow - always use WHERE clauses
- Distinct counts are more expensive than simple counts
- Complex nested aggregations should be avoided or split into multiple queries
- Consider using materialized views for frequently accessed aggregations
- Time-range queries on timestamp fields are well-optimized

## Combining Aggregates with Details

```graphql
query DashboardMetrics($limit: Int!) {
  # Overall statistics
  metrics: user_transactions_aggregate {
    aggregate {
      count
      avg { gas_used }
      sum { gas_used }
    }
  }

  # Top performers
  top_users: user_transactions(
    order_by: { gas_used: desc },
    limit: $limit,
    distinct_on: sender
  ) {
    sender
    gas_used
  }

  # Recent activity
  recent: user_transactions(
    order_by: { timestamp: desc },
    limit: $limit
  ) {
    hash
    sender
    timestamp
    success
  }
}
```

## TypeScript Integration

```typescript
import { ApolloClient, gql } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql"
});

const TRANSACTION_STATS = gql`
  query TransactionStats {
    user_transactions_aggregate {
      aggregate {
        count
        avg { gas_used }
      }
    }
  }
`;

const { data } = await client.query({ query: TRANSACTION_STATS });
console.log(`Total transactions: ${data.user_transactions_aggregate.aggregate.count}`);
console.log(`Average gas: ${data.user_transactions_aggregate.aggregate.avg.gas_used}`);
```

## Related Concepts

- [GraphQL Overview](https://www.dwellir.com/docs/aptos/graphql/overview) - Introduction to GraphQL indexer
- [User Transactions](https://www.dwellir.com/docs/aptos/user_transactions) - Query transaction data
- [Token Activities](https://www.dwellir.com/docs/aptos/token_activities) - Aggregate token transfers
- [ANS Queries](https://www.dwellir.com/docs/aptos/ans_queries) - Query naming service data

---

## aggregator_v2

# aggregator_v2

Aggregator V2 is a specialized data structure in Aptos designed for high-performance concurrent counters that enable parallel transaction execution without conflicts. It leverages Aptos's Block-STM parallel execution engine to allow multiple transactions to increment or decrement the same counter simultaneously, dramatically improving throughput for applications with shared state.

## Overview

Traditional blockchain counters create transaction conflicts when multiple operations try to modify the same value concurrently. Aggregator V2 solves this by using commutative merge semantics, where operations can be applied in any order and combined at the end of block execution. This enables true parallelism for operations like tracking token supply, counting users, or maintaining statistics.

## Technical Implementation

Aggregator V2 uses a mathematical property called commutativity: addition and subtraction operations produce the same result regardless of execution order. The Block-STM engine tracks delta values during parallel execution and merges them atomically at commit time.

```move
module 0x1::token_counter {
    use aptos_framework::aggregator_v2::{Self, Aggregator};

    struct TokenStats has key {
        total_minted: Aggregator<u64>,
        total_burned: Aggregator<u64>,
        active_holders: Aggregator<u64>
    }

    public fun initialize(account: &signer) {
        move_to(account, TokenStats {
            total_minted: aggregator_v2::create_aggregator(0),
            total_burned: aggregator_v2::create_aggregator(0),
            active_holders: aggregator_v2::create_aggregator(0)
        });
    }

    public entry fun mint_tokens(amount: u64) acquires TokenStats {
        let stats = borrow_global_mut<TokenStats>(@0x1);
        aggregator_v2::add(&mut stats.total_minted, amount);
    }

    public entry fun burn_tokens(amount: u64) acquires TokenStats {
        let stats = borrow_global_mut<TokenStats>(@0x1);
        aggregator_v2::add(&mut stats.total_burned, amount);
    }

    public entry fun on_new_holder() acquires TokenStats {
        let stats = borrow_global_mut<TokenStats>(@0x1);
        aggregator_v2::add(&mut stats.active_holders, 1);
    }

    #[view]
    public fun get_total_minted(): u64 acquires TokenStats {
        let stats = borrow_global<TokenStats>(@0x1);
        aggregator_v2::read(&stats.total_minted)
    }

    #[view]
    public fun circulating_supply(): u64 acquires TokenStats {
        let stats = borrow_global<TokenStats>(@0x1);
        aggregator_v2::read(&stats.total_minted) - aggregator_v2::read(&stats.total_burned)
    }
}
```

## API Functions

```move
// Create new aggregator with initial value
aggregator_v2::create_aggregator<T>(initial_value: T): Aggregator<T>

// Add value (works for any numeric type)
aggregator_v2::add<T>(aggregator: &mut Aggregator<T>, value: T)

// Subtract value
aggregator_v2::sub<T>(aggregator: &mut Aggregator<T>, value: T)

// Read current value
aggregator_v2::read<T>(aggregator: &Aggregator<T>): T

// Try to subtract with bounds checking
aggregator_v2::try_sub<T>(aggregator: &mut Aggregator<T>, value: T): bool
```

## Real-World Use Cases

1. **Token Supply Tracking**: Track total minted and burned tokens across thousands of concurrent mint/burn transactions without creating bottlenecks or conflicts.

2. **User Statistics**: Maintain real-time counts of active users, daily transactions, or engagement metrics in high-traffic applications without serialization.

3. **DEX Volume Counters**: Aggregate trading volumes, swap counts, and liquidity metrics across parallel trading operations on decentralized exchanges.

4. **NFT Collection Stats**: Track total minted NFTs, active listings, and collection metrics as multiple users mint and trade simultaneously.

5. **Gaming Leaderboards**: Update player scores, achievement counts, and global statistics with high concurrency during peak gaming periods.

6. **Protocol Analytics**: Maintain real-time protocol metrics like total value locked, transaction counts, and fee accumulation without performance degradation.

## Performance Benefits

Without Aggregator V2, concurrent counter updates would create conflicts requiring sequential execution:

```move
// Traditional counter - causes conflicts
struct OldCounter has key {
    value: u64  // Every update conflicts with others
}

public fun increment() acquires OldCounter {
    let counter = borrow_global_mut<OldCounter>(@0x1);
    counter.value = counter.value + 1;  // Conflict!
}
```

With Aggregator V2, the same operations execute in parallel:

```move
// Parallel counter - no conflicts
struct NewCounter has key {
    value: Aggregator<u64>  // Parallel updates merge
}

public fun increment() acquires NewCounter {
    let counter = borrow_global_mut<NewCounter>(@0x1);
    aggregator_v2::add(&mut counter.value, 1);  // Parallelizes!
}
```

Benchmark results show 10-100x throughput improvements for counter-heavy workloads.

## Best Practices

**Use for Shared State**: Apply Aggregator V2 to any counter or accumulator that multiple transactions will modify concurrently.

**Combine with Regular Fields**: Mix aggregators with normal fields in the same struct for optimal performance:

```move
struct MixedStats has key {
    total_count: Aggregator<u64>,  // Parallel updates
    last_updated: u64,             // Sequential field
    admin: address                 // Sequential field
}
```

**Read Sparingly in Transactions**: Reading aggregator values during transaction execution may reduce parallelism. Prefer reads in view functions.

**Batch Operations**: When possible, batch multiple small updates into larger operations to reduce overhead.

**Consider Overflow**: While aggregators support large numbers, implement checks for meaningful limits based on your application logic.

**Monitor Performance**: Use Aptos performance metrics to verify that aggregators are providing expected parallelism benefits.

## Limitations and Considerations

- Aggregators only support commutative operations (addition, subtraction)
- Cannot use aggregators for operations requiring specific ordering
- Reading aggregator values in transaction execution may impact parallelism
- Not suitable for operations requiring immediate consistency checks
- Best suited for statistics and metrics rather than critical balance tracking

## Migration from V1

If you're using the older Aggregator V1 API, migrate to V2 for improved performance:

```move
// V1 (deprecated)
use aptos_framework::aggregator;
let agg = aggregator::create(100); // With max limit

// V2 (recommended)
use aptos_framework::aggregator_v2;
let agg = aggregator_v2::create_aggregator(0); // No max limit needed
```

## Related Concepts

- [Block-STM](https://medium.com/aptoslabs/block-stm-how-we-execute-over-160k-transactions-per-second-on-the-aptos-blockchain-3b003657e4ba) - Parallel execution engine
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Managing shared state
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Test parallel execution scenarios
- [View Functions](https://www.dwellir.com/docs/aptos/view_functions) - Read aggregator values efficiently

---

## ans_queries

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# ans_queries

Aptos Name Service (ANS) provides human-readable names for Aptos addresses, similar to DNS for the internet. The GraphQL API enables efficient querying of ANS registrations, allowing applications to resolve names to addresses, lookup reverse mappings, check expiration dates, and manage domain portfolios programmatically.

## Overview

ANS transforms complex hexadecimal addresses like `0x1a2b3c...` into memorable names like `alice.apt`, significantly improving user experience. The GraphQL indexer provides indexed access to ANS data, enabling fast lookups for wallet displays, payment systems, social features, and any application needing human-readable identifiers.

## Core Query Patterns

### Resolve Name to Address

```graphql
query ResolveName($name: String!) {
  ans_lookup(where: { name: { _eq: $name } }) {
    name
    address
    expiration_timestamp
    registered_at
    owner
  }
}
```

### Reverse Lookup (Address to Names)

```graphql
query AddressToNames($address: String!) {
  ans_lookup(
    where: { address: { _eq: $address } },
    order_by: { registered_at: desc }
  ) {
    name
    expiration_timestamp
    is_primary
  }
}
```

### Check Name Availability

```graphql
query CheckAvailability($name: String!) {
  ans_lookup(
    where: {
      name: { _eq: $name },
      expiration_timestamp: { _gt: "now()" }
    }
  ) {
    name
    owner
  }
}
```

### Get Primary Name

```graphql
query GetPrimaryName($address: String!) {
  ans_lookup(
    where: {
      address: { _eq: $address },
      is_primary: { _eq: true }
    },
    limit: 1
  ) {
    name
    expiration_timestamp
  }
}
```

### Search Names by Pattern

```graphql
query SearchNames($pattern: String!, $limit: Int!) {
  ans_lookup(
    where: { name: { _like: $pattern } },
    order_by: { registered_at: desc },
    limit: $limit
  ) {
    name
    address
    owner
    registered_at
  }
}
```

### Expiring Names

```graphql
query ExpiringNames($days: Int!) {
  ans_lookup(
    where: {
      expiration_timestamp: {
        _gte: "now()",
        _lte: "now() + ${days} days"
      }
    },
    order_by: { expiration_timestamp: asc }
  ) {
    name
    address
    expiration_timestamp
  }
}
```

## Real-World Use Cases

1. **Wallet Applications**: Display user-friendly names instead of addresses in transaction histories, contact lists, and payment interfaces for improved UX.

2. **Payment Systems**: Allow users to send payments to names like "alice.apt" instead of copying long addresses, reducing errors and improving accessibility.

3. **Social Platforms**: Enable username-based social features where users can follow, message, or interact with others using memorable names.

4. **Domain Marketplaces**: Build platforms for buying, selling, and trading ANS domains with search, filtering, and expiration monitoring.

5. **Portfolio Management**: Track domain portfolios, monitor expiration dates, and manage renewal workflows for users with multiple names.

6. **Identity Verification**: Use ANS as a lightweight identity system where verified names provide reputation and trust signals.

## Best Practices

**Cache Resolved Names**: ANS data changes infrequently - implement caching with appropriate TTLs to reduce API calls.

**Handle Non-Existent Names**: Always check for null/empty results when resolving names and provide clear user feedback.

**Validate Name Format**: Implement client-side name validation (format, length, allowed characters) before querying.

**Check Expiration**: Always verify expiration\_timestamp to ensure names are still active before relying on them.

**Support Both Directions**: Implement both name-to-address and address-to-name lookups for complete functionality.

**Prioritize Primary Names**: When an address owns multiple names, prefer displaying the primary name for consistency.

**Batch Lookups**: When resolving multiple names, batch them into single GraphQL queries for efficiency.

## TypeScript Integration

```typescript
import { ApolloClient, gql } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql"
});

async function resolveName(name: string): Promise<string | null> {
  const { data } = await client.query({
    query: gql`
      query ResolveName($name: String!) {
        ans_lookup(where: { name: { _eq: $name } }) {
          address
          expiration_timestamp
        }
      }
    `,
    variables: { name }
  });

  const result = data.ans_lookup[0];
  if (!result) return null;

  const now = Date.now();
  const expiration = new Date(result.expiration_timestamp).getTime();

  return expiration > now ? result.address : null;
}

async function getPrimaryName(address: string): Promise<string | null> {
  const { data } = await client.query({
    query: gql`
      query GetPrimaryName($address: String!) {
        ans_lookup(
          where: {
            address: { _eq: $address },
            is_primary: { _eq: true }
          },
          limit: 1
        ) {
          name
          expiration_timestamp
        }
      }
    `,
    variables: { address }
  });

  const result = data.ans_lookup[0];
  if (!result) return null;

  const now = Date.now();
  const expiration = new Date(result.expiration_timestamp).getTime();

  return expiration > now ? result.name : null;
}
```

## Advanced Queries

### Domain Portfolio Analysis

```graphql
query PortfolioStats($owner: String!) {
  active: ans_lookup_aggregate(
    where: {
      owner: { _eq: $owner },
      expiration_timestamp: { _gt: "now()" }
    }
  ) {
    aggregate { count }
  }

  expiring_soon: ans_lookup_aggregate(
    where: {
      owner: { _eq: $owner },
      expiration_timestamp: {
        _gt: "now()",
        _lte: "now() + 30 days"
      }
    }
  ) {
    aggregate { count }
  }

  domains: ans_lookup(
    where: { owner: { _eq: $owner } },
    order_by: { expiration_timestamp: asc }
  ) {
    name
    address
    expiration_timestamp
    registered_at
  }
}
```

### Marketplace Trending Names

```graphql
query TrendingNames($hours: Int!) {
  ans_lookup(
    where: {
      registered_at: { _gte: "now() - ${hours} hours" }
    },
    order_by: { registered_at: desc },
    limit: 50
  ) {
    name
    address
    owner
    registered_at
  }
}
```

## Common Patterns

```typescript
// Display name with fallback to address
function displayName(address: string, ansName?: string | null): string {
  if (ansName) return ansName;
  return `${address.substring(0, 6)}...${address.substring(address.length - 4)}`;
}

// Validate name format
function isValidANSName(name: string): boolean {
  return /^[a-z0-9-]{1,63}\.apt$/.test(name);
}

// Check if name is expired
function isExpired(expirationTimestamp: string): boolean {
  return new Date(expirationTimestamp).getTime() < Date.now();
}
```

## Related Concepts

- [GraphQL Overview](https://www.dwellir.com/docs/aptos/graphql/overview) - GraphQL indexer introduction
- [Aggregations](https://www.dwellir.com/docs/aptos/aggregations) - Aggregate ANS statistics
- [User Transactions](https://www.dwellir.com/docs/aptos/user_transactions) - Track ANS registrations
- [Subscriptions](https://www.dwellir.com/docs/aptos/subscriptions) - Real-time ANS updates

---

## authentication

> Coming soon: Need support for this? Email <support@dwellir.com> if you want early access

# authentication

Streaming API authentication provides secure access control for gRPC transaction streams, ensuring only authorized clients can subscribe to blockchain data feeds. Proper authentication is essential for production deployments, rate limiting, access control, and preventing unauthorized resource consumption.

## Overview

The Aptos streaming service uses bearer token authentication negotiated during channel setup. This approach provides security without sacrificing performance, allowing long-lived streaming connections while maintaining proper access controls. Authentication tokens are validated once during connection establishment and remain valid for the duration of the stream.

## Authentication Methods

### Bearer Token Authentication

```typescript
import { credentials, Metadata } from "@grpc/grpc-js";

// Create metadata with API key
const metadata = new Metadata();
metadata.add("authorization", `Bearer ${process.env.API_KEY}`);

// Establish authenticated connection
const client = new TransactionStreamClient(
  "stream.aptos.dwellir.com:443",
  credentials.createSsl(),
  {
    channelOverride: channel
  }
);

// Use metadata in stream requests
const stream = client.subscribe(request, metadata);
```

### Mutual TLS (mTLS)

For enterprise deployments, mTLS provides certificate-based authentication:

```typescript
import * as fs from "fs";

const rootCert = fs.readFileSync("ca-cert.pem");
const clientKey = fs.readFileSync("client-key.pem");
const clientCert = fs.readFileSync("client-cert.pem");

const sslCredentials = credentials.createSsl(
  rootCert,
  clientKey,
  clientCert
);

const client = new TransactionStreamClient(
  "stream.aptos.dwellir.com:443",
  sslCredentials
);
```

## Real-World Use Cases

1. **Production Services**: Secure streaming connections for production applications handling sensitive transaction data or serving multiple users.

2. **Multi-Tenant Systems**: Isolate stream access per customer using separate API keys with individual rate limits and quotas.

3. **Analytics Platforms**: Authenticate data ingestion pipelines that process blockchain streams for business intelligence and reporting.

4. **Trading Systems**: Secure real-time market data feeds for trading bots and algorithmic trading systems with guaranteed access.

5. **Compliance Monitoring**: Authenticate regulatory compliance systems that monitor transactions for suspicious activities or reporting requirements.

6. **Enterprise Infrastructure**: Integrate blockchain streams into corporate systems with certificate-based authentication and access controls.

## Best Practices

**Rotate Keys Regularly**: Implement automated key rotation schedules to minimize exposure if credentials are compromised.

**Use Environment Variables**: Never hard-code API keys or certificates - store them securely in environment variables or secrets management systems.

**Implement Retry Logic**: Handle authentication failures gracefully with exponential backoff and automatic retry mechanisms.

**Monitor Authentication Status**: Track authentication failures and unusual patterns that might indicate security issues.

**Separate Keys Per Environment**: Use different API keys for development, staging, and production to prevent accidental production access.

**Implement Timeout Handling**: Set appropriate timeout values for authentication handshakes to fail fast on connection issues.

**Log Security Events**: Maintain audit logs of authentication attempts, failures, and key usage for security monitoring.

## Connection Management

```typescript
class AuthenticatedStreamClient {
  private client: TransactionStreamClient;
  private metadata: Metadata;

  constructor(apiKey: string) {
    this.metadata = new Metadata();
    this.metadata.add("authorization", `Bearer ${apiKey}`);

    this.client = new TransactionStreamClient(
      "stream.aptos.dwellir.com:443",
      credentials.createSsl()
    );
  }

  subscribe(request: SubscribeRequest): ClientReadableStream {
    return this.client.subscribe(request, this.metadata);
  }

  // Handle authentication errors
  private handleAuthError(error: Error) {
    if (error.message.includes("Unauthenticated")) {
      console.error("Authentication failed - check API key");
      // Trigger key refresh or alert
    }
  }
}
```

## Security Considerations

**TLS Encryption**: Always use TLS/SSL encryption for streaming connections to protect credentials and data in transit.

**Key Storage**: Store API keys and certificates in secure vaults (HashiCorp Vault, AWS Secrets Manager, etc.) rather than configuration files.

**Access Scope**: Request minimum necessary permissions for API keys - don't use admin keys for read-only streaming.

**Expiration Policies**: Implement key expiration policies and automated renewal processes to maintain security hygiene.

**Rate Limiting**: Understand rate limits associated with your API keys and implement client-side throttling.

**Error Handling**: Don't expose authentication details in error messages or logs that might leak credentials.

## Troubleshooting

### Authentication Failures

```typescript
stream.on("error", (error) => {
  if (error.code === grpc.status.UNAUTHENTICATED) {
    console.error("Authentication failed");
    // Check API key validity
    // Verify key has required permissions
    // Confirm key hasn't expired
  }
});
```

### Connection Issues

```typescript
const connectionDeadline = new Date(Date.now() + 5000);

const stream = client.subscribe(request, metadata, {
  deadline: connectionDeadline
});

stream.on("status", (status) => {
  if (status.code !== grpc.status.OK) {
    console.error(`Connection failed: ${status.details}`);
  }
});
```

## Related Concepts

- [Streaming Overview](https://www.dwellir.com/docs/aptos/streaming/overview) - Introduction to transaction streaming
- [Real-Time Streaming](https://www.dwellir.com/docs/aptos/real_time) - Live transaction processing
- [Custom Processors](https://www.dwellir.com/docs/aptos/custom_processors) - Building stream processors
- [Historical Replay](https://www.dwellir.com/docs/aptos/historical_replay) - Replaying past transactions

---

## blocks_by_height

# blocks_by_height

## Overview

Fetch a block by its block height together with block metadata, and optionally expand the block into the transactions it contains. This is the right endpoint when you already know the block you want and need to anchor application logic to block boundaries instead of individual transaction hashes.

## Endpoint

`GET /v1/blocks/by_height/{block_height}`

`block_height` identifies the consensus block, not a single ledger version. One block can include multiple transactions, so this endpoint is useful for explorers, replay jobs, and monitoring systems that checkpoint work at block level.

## Request Parameters

- `block_height` (`string, required`): Path parameter: Aptos block height to retrieve
- `with_transactions` (`boolean, optional`): Query parameter: Include full transaction objects instead of block metadata alone

## Request Example

```bash
# Get block metadata only
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/blocks/by_height/1000000" \
      -H "Accept: application/json"

    # Get block with all transactions
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/blocks/by_height/1000000?with_transactions=true" \
      -H "Accept: application/json"
```

## Response Fields

- `result` (`OBJECT, required`): With `with_transactions=false`, the response stays compact and is best for checkpointing or timeline views: ```json { "block_height": "1000000", "block_hash": "0xd8f4f8cb...", "first_version": "10240841", "last_version": "10240863", "block_timestamp": "1661961900602338", "transactions": null } ``` When `with_transactions=true`, the response also includes the committed transactions inside that block. That is useful for replay or block explorer detail pages, but it can grow large on busy blocks.

## Successful Response

```json
{
  "block_height": "1000000",
  "block_hash": "0xd8f4f8cb...",
  "first_version": "10240841",
  "last_version": "10240863",
  "block_timestamp": "1661961900602338",
  "transactions": null
}
```

## Response Shape

With `with_transactions=false`, the response stays compact and is best for checkpointing or timeline views:

```json
{
  "block_height": "1000000",
  "block_hash": "0xd8f4f8cb...",
  "first_version": "10240841",
  "last_version": "10240863",
  "block_timestamp": "1661961900602338",
  "transactions": null
}
```

When `with_transactions=true`, the response also includes the committed transactions inside that block. That is useful for replay or block explorer detail pages, but it can grow large on busy blocks.

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Get block metadata only
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/blocks/by_height/1000000" \
  -H "Accept: application/json"

# Get block with all transactions
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/blocks/by_height/1000000?with_transactions=true" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Metadata only
block = client.block_by_height(1_000_000, with_transactions=False)
print(f"Block {block['block_height']}: versions {block['first_version']}-{block['last_version']}")

# With transactions
block = client.block_by_height(1_000_000, with_transactions=True)
for txn in block.get("transactions", []):
    print(f"  Version {txn['version']}: {txn['vm_status']}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Get block with transactions
const block = await aptos.getBlockByHeight({
  blockHeight: 1_000_000n,
  options: { withTransactions: true }
});
console.log(`Block ${block.block_height}: ${block.transactions?.length} txns`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

// Get block with transactions
let block = client.get_block_by_height(1_000_000, true).await?;
println!("Block hash: {}", block.inner().block_hash);
println!("Versions: {}-{}", block.inner().first_version, block.inner().last_version);
```

## When to Enable `with_transactions`

- Set it to `false` when you only need block boundaries, timestamps, or version ranges.
- Set it to `true` when your worker will immediately process the block's transactions and you want to avoid extra follow-up lookups.
- Prefer metadata-only reads for polling or checkpoint loops, then expand only the blocks you actually need to inspect in detail.

## Practical Guidance

- Store both `block_height` and the `first_version`/`last_version` range if you plan to resume block-based indexing later. Read `block_timestamp` from the metadata response rather than assuming a generic `timestamp` field.
- Busy blocks can return large payloads, so avoid defaulting to `with_transactions=true` in lightweight monitoring jobs.
- If your UI deep-links into one transaction from the block, pair this endpoint with [`transactions_by_hash`](https://www.dwellir.com/docs/aptos/transactions_by_hash) for the detailed drill-down.
- Treat block height and ledger version as different coordinates. A version points to one transaction; a block height points to a grouped execution boundary.

## Related Endpoints

- [accounts\_transactions](https://www.dwellir.com/docs/aptos/accounts_transactions) for sender-scoped history
- [transactions\_by\_hash](https://www.dwellir.com/docs/aptos/transactions_by_hash) for one known transaction

---

## blocks_by_version

# blocks_by_version

## Overview

Fetch the block that contains a given ledger version. This endpoint is useful when you have a transaction version and need to understand its block context, including block metadata, timestamp, and related transactions.

## Endpoint

`GET /v1/blocks/by_version/{version}`

## Request

### Path Parameters

| Name    | Type   | Required | Description                          |
| ------- | ------ | -------- | ------------------------------------ |
| version | string | Yes      | Ledger version (transaction version) |

### Query Parameters

| Name               | Type    | Required | Description                               |
| ------------------ | ------- | -------- | ----------------------------------------- |
| with\_transactions | boolean | No       | Include full transaction data in response |

## Response

### Success Response (200)

Returns a block object containing:

```json
{
  "block_height": "1000000",
  "block_hash": "0x...",
  "block_timestamp": "1234567890",
  "first_version": "123456700",
  "last_version": "123456789",
  "transactions": [...]
}
```

When `with_transactions=true`, the transactions array contains complete transaction objects. Without this parameter, only block metadata is returned.

### Error Responses

| Status | Error Code          | Description               |
| ------ | ------------------- | ------------------------- |
| 400    | invalid\_input      | Invalid version format    |
| 404    | version\_not\_found | Version doesn't exist yet |
| 500    | internal\_error     | Server error              |

## Code Examples

```bash
curl -X GET https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/blocks/by_version/123456789?with_transactions=true \
  -H "Accept: application/json"
```

## Use Cases

This endpoint serves several important purposes in blockchain development:

1. **Transaction Context Discovery**: Given a specific transaction version, retrieve its block context to understand when and how it was processed, including block timestamp and proposer information.

2. **Block Explorer Implementation**: Build block explorers that allow users to navigate from individual transactions to their containing blocks, showing all transactions processed together.

3. **Temporal Analysis**: Analyze transaction ordering and timing within blocks to understand MEV opportunities, transaction dependencies, or validator behavior.

4. **Event Correlation**: When investigating events across multiple transactions, use this endpoint to group related transactions that were executed in the same block.

5. **Audit Trail Construction**: Create comprehensive audit trails by linking transactions to their block context, providing immutable timestamps and ordering guarantees.

6. **State Snapshot Coordination**: Coordinate state snapshots across multiple accounts or resources by identifying block boundaries that represent consistent global state.

## Best Practices

**Version vs Height**: This endpoint uses transaction version (ledger version), not block height. Transaction versions are sequential across all transactions, while block heights increment per block. Use `blocks/by_height` if you have a block height instead.

**Pagination Strategy**: When `with_transactions=true`, large blocks can return megabytes of data. If you need to process many blocks, consider fetching metadata first, then selectively fetching transaction details only when needed.

**Caching**: Block data is immutable once finalized. Implement aggressive caching for historical blocks (older than 100 versions from chain head) to minimize API load.

**Version Boundaries**: Each block contains a range of versions from `first_version` to `last_version`. To process all transactions in a block, iterate through this range using individual transaction queries if needed.

## Performance Considerations

Queries without `with_transactions` are fast and lightweight (typically under 50ms). Including full transaction data can increase response time to 200-500ms for blocks with many transactions, depending on transaction complexity and payload sizes. The response size can range from a few KB (metadata only) to several MB (large blocks with full transaction data).

For high-throughput applications processing many blocks sequentially, consider using the transaction streaming API instead of polling individual blocks.

---

## custom_processors

> Coming soon: Need support for this? Email <support@dwellir.com> if you want early access

# custom_processors

Custom processors transform raw blockchain transactions into application-specific data models, enabling efficient real-time indexing and analytics. Building custom processors allows you to extract exactly the information your application needs while filtering irrelevant data, creating optimized databases tailored to your use cases.

## Overview

A custom processor subscribes to transaction streams, decodes relevant transactions and events, transforms the data into your application's schema, and writes it to your database. This approach is more efficient than polling APIs or running full nodes, providing millisecond-latency updates with minimal infrastructure overhead.

## Processor Architecture

```typescript
class AptosStreamProcessor {
  private stream: ClientReadableStream;
  private database: Database;

  async start() {
    this.stream = this.subscribeToTransactions();

    this.stream.on("data", async (transaction) => {
      try {
        await this.processTransaction(transaction);
      } catch (error) {
        console.error("Processing error:", error);
        this.handleError(error, transaction);
      }
    });

    this.stream.on("error", (error) => {
      console.error("Stream error:", error);
      this.reconnect();
    });

    this.stream.on("end", () => {
      console.log("Stream ended");
      this.reconnect();
    });
  }

  private async processTransaction(tx: Transaction) {
    // Filter transactions by type
    if (!this.shouldProcess(tx)) return;

    // Decode transaction payload
    const decoded = this.decodeTransaction(tx);

    // Transform to application model
    const model = this.transformToModel(decoded);

    // Write to database
    await this.database.insert(model);

    // Update indexes
    await this.updateIndexes(model);

    // Emit events for real-time features
    this.emit("transaction", model);
  }

  private shouldProcess(tx: Transaction): boolean {
    // Filter logic - only process relevant transactions
    return (
      tx.type === "user_transaction" &&
      tx.payload?.function?.startsWith("0x1::coin::transfer")
    );
  }
}
```

## Real-World Use Cases

1. **NFT Marketplace Indexing**: Process mint, transfer, and sale events in real-time to keep marketplace listings and analytics up-to-date without polling.

2. **DeFi Protocol Analytics**: Track swap events, liquidity changes, and yield updates across multiple protocols for portfolio dashboards and price feeds.

3. **Wallet Transaction History**: Index all transactions for user addresses into optimized databases for instant transaction history queries.

4. **Gaming State Management**: Process game move events and state changes to maintain real-time leaderboards and player inventories.

5. **Compliance Monitoring**: Scan transaction streams for patterns matching compliance rules, flagging suspicious activities in real-time.

6. **Price Oracle Updates**: Extract DEX swap data to compute and publish price feeds with sub-second latency for DeFi applications.

## Best Practices

**Idempotent Processing**: Design processors to handle duplicate events safely since streaming guarantees at-least-once delivery, not exactly-once.

**Checkpoint Progress**: Persistently track the last processed transaction version to enable resumption after restarts without reprocessing.

**Batch Database Writes**: Buffer decoded events and write in batches to reduce database load and improve throughput.

**Handle Reorgs Carefully**: Although rare on Aptos, implement logic to handle chain reorganizations if processing near the chain head.

**Monitor Performance**: Track processing latency, throughput, and error rates to detect bottlenecks and degradations early.

**Implement Circuit Breakers**: Automatically pause processing when error rates exceed thresholds to prevent cascading failures.

**Schema Versioning**: Plan for schema evolution - use versioned data models to support processor upgrades without downtime.

## Event Decoding

```typescript
interface DecodedEvent {
  type: string;
  data: any;
  address: string;
  sequence: bigint;
}

function decodeEvent(event: Event): DecodedEvent {
  const eventType = event.type.name;

  switch (eventType) {
    case "0x1::coin::WithdrawEvent":
      return {
        type: "coin_withdraw",
        data: {
          amount: BigInt(event.data.amount),
          coinType: event.type.typeArgs[0]
        },
        address: event.key.accountAddress,
        sequence: BigInt(event.sequenceNumber)
      };

    case "0x1::coin::DepositEvent":
      return {
        type: "coin_deposit",
        data: {
          amount: BigInt(event.data.amount),
          coinType: event.type.typeArgs[0]
        },
        address: event.key.accountAddress,
        sequence: BigInt(event.sequenceNumber)
      };

    default:
      return null;
  }
}
```

## State Management

```typescript
class ProcessorState {
  private currentVersion: bigint = 0n;
  private checkpointInterval: number = 100;

  async saveCheckpoint() {
    await this.database.upsert("processor_state", {
      name: "main_processor",
      last_version: this.currentVersion.toString(),
      timestamp: new Date()
    });
  }

  async loadCheckpoint(): Promise<bigint> {
    const state = await this.database.findOne("processor_state", {
      name: "main_processor"
    });

    return state ? BigInt(state.last_version) : 0n;
  }

  async updateVersion(version: bigint) {
    this.currentVersion = version;

    // Periodic checkpointing
    if (version % BigInt(this.checkpointInterval) === 0n) {
      await this.saveCheckpoint();
    }
  }
}
```

## Error Handling

```typescript
class ProcessorErrorHandler {
  private errorCounts: Map<string, number> = new Map();
  private maxRetries: number = 3;

  async handleProcessingError(error: Error, tx: Transaction) {
    const txHash = tx.hash;
    const count = (this.errorCounts.get(txHash) || 0) + 1;

    if (count >= this.maxRetries) {
      // Log to dead letter queue
      await this.deadLetterQueue.push({
        transaction: tx,
        error: error.message,
        attempts: count,
        timestamp: new Date()
      });

      this.errorCounts.delete(txHash);
      return;
    }

    this.errorCounts.set(txHash, count);

    // Exponential backoff
    await this.sleep(Math.pow(2, count) * 1000);

    // Retry processing
    await this.processTransaction(tx);
  }

  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
```

## Performance Optimization

- **Parallel Processing**: Process independent transactions concurrently using worker pools
- **Database Connection Pooling**: Reuse database connections to reduce overhead
- **Selective Decoding**: Only decode fields you need rather than full transaction payloads
- **Caching**: Cache frequently accessed data (token metadata, account info) to reduce database queries
- **Compression**: Compress large payloads before storage to save space and I/O

## Monitoring Metrics

```typescript
class ProcessorMetrics {
  transactionsProcessed: number = 0;
  eventsDecoded: number = 0;
  processingLatency: number[] = [];
  errorCount: number = 0;

  recordProcessing(startTime: number) {
    this.transactionsProcessed++;
    this.processingLatency.push(Date.now() - startTime);

    // Keep only recent latencies
    if (this.processingLatency.length > 1000) {
      this.processingLatency.shift();
    }
  }

  getMetrics() {
    return {
      total: this.transactionsProcessed,
      errors: this.errorCount,
      avgLatency: this.processingLatency.reduce((a, b) => a + b, 0) / this.processingLatency.length,
      throughput: this.transactionsProcessed / (Date.now() / 1000)
    };
  }
}
```

## Related Concepts

- [Streaming Overview](https://www.dwellir.com/docs/aptos/streaming/overview) - Introduction to streaming
- [Historical Replay](https://www.dwellir.com/docs/aptos/historical_replay) - Backfilling historical data
- [Real-Time Streaming](https://www.dwellir.com/docs/aptos/real_time) - Live transaction processing
- [Authentication](https://www.dwellir.com/docs/aptos/authentication) - Securing stream access

---

## entry_functions

# entry_functions

## Overview

Entry functions are the primary mechanism for executing Move code via transactions on Aptos. These special functions serve as transaction entry points, allowing external accounts to trigger on-chain logic execution.

## Technical Details

Entry functions are declared with the `public entry` visibility modifier in Move modules:

```move
public entry fun transfer(from: &signer, to: address, amount: u64) {
    // Function implementation
}
```

**Key Characteristics**:

- Must be marked with both `public` and `entry` keywords
- Can be called directly from transactions
- Cannot return values (void return type)
- Can modify blockchain state
- Execute with the transaction sender's authority
- Support generic type parameters

## How Entry Functions Work

When a transaction calls an entry function:

1. The Move VM loads the module containing the function
2. Arguments are BCS-deserialized from transaction payload
3. The function executes with sender's signer capability
4. State changes are committed if execution succeeds
5. Events are emitted to the blockchain
6. Gas is consumed based on computational cost

Entry functions form the external API of Move modules, bridging off-chain applications with on-chain logic.

## Practical Examples

**Simple Transfer:**

```move
public entry fun simple_transfer(sender: &signer, recipient: address, amount: u64) {
    coin::transfer<AptosCoin>(sender, recipient, amount);
}
```

**With Type Parameters:**

```move
public entry fun swap<CoinTypeA, CoinTypeB>(
    sender: &signer,
    amount_in: u64,
    min_amount_out: u64
) {
    // DEX swap implementation
}
```

**Multiple Operations:**

```move
public entry fun batch_mint(admin: &signer, recipients: vector<address>, amounts: vector<u64>) {
    let len = vector::length(&recipients);
    let i = 0;
    while (i < len) {
        let recipient = *vector::borrow(&recipients, i);
        let amount = *vector::borrow(&amounts, i);
        mint_to(admin, recipient, amount);
        i = i + 1;
    };
}
```

## Use Cases

Entry functions power diverse blockchain applications:

1. **Token Operations**: Transfers, minting, burning, and approvals for fungible and non-fungible tokens.

2. **DeFi Protocols**: Swaps, liquidity provision, staking, and yield farming operations.

3. **NFT Marketplaces**: Listing, purchasing, bidding, and collection management.

4. **Gaming**: Character creation, item crafting, battle execution, and reward distribution.

5. **Governance**: Proposal submission, voting, and execution of governance decisions.

6. **Identity**: Account creation, key rotation, and permission management.

## Best Practices

**Input Validation**: Always validate inputs within entry functions to prevent invalid state transitions. Check address validity, amount ranges, and permission requirements explicitly.

**Avoid Write Conflicts**: Under Block-STM parallel execution, minimize conflicts by:

- Reading shared resources early in execution
- Writing to unique per-account resources when possible
- Using fine-grained resource structures instead of global state
- Leveraging Aggregator V2 for concurrent counters

**Gas Efficiency**: Keep entry functions focused and delegate complex logic to internal helper functions. This improves code organization and allows better gas optimization.

**Error Handling**: Use descriptive abort codes and messages to help developers debug failed transactions:

```move
const EINSUFFICIENT_BALANCE: u64 = 1;
const EINVALID_RECIPIENT: u64 = 2;

public entry fun safe_transfer(sender: &signer, to: address, amount: u64) {
    assert!(to != @0x0, EINVALID_RECIPIENT);
    assert!(coin::balance<AptosCoin>(signer::address_of(sender)) >= amount, EINSUFFICIENT_BALANCE);
    coin::transfer<AptosCoin>(sender, to, amount);
}
```

**Signer Requirement**: Only include `&signer` parameters for accounts that must authorize the transaction. Extra signer parameters complicate multi-signature scenarios unnecessarily.

**Event Emission**: Emit events for significant state changes to enable off-chain indexing and monitoring:

```move
event::emit(TransferEvent {
    from: signer::address_of(sender),
    to: recipient,
    amount
});
```

## Common Patterns

**Admin Functions**: Restrict privileged operations with capability checks:

```move
public entry fun admin_mint(admin: &signer, to: address, amount: u64) acquires AdminCap {
    let admin_addr = signer::address_of(admin);
    assert!(exists<AdminCap>(admin_addr), ENOT_ADMIN);
    // Mint logic
}
```

**Batch Operations**: Process multiple operations in one transaction for efficiency:

```move
public entry fun batch_transfer(sender: &signer, recipients: vector<address>, amounts: vector<u64>) {
    let len = vector::length(&recipients);
    let i = 0;
    while (i < len) {
        coin::transfer<AptosCoin>(sender, *vector::borrow(&recipients, i), *vector::borrow(&amounts, i));
        i = i + 1;
    };
}
```

**Resource Initialization**: Create and move resources to accounts:

```move
public entry fun initialize_account(account: &signer) {
    move_to(account, AccountData {
        balance: 0,
        nonce: 0
    });
}
```

## Anti-Patterns to Avoid

- **Returning Values**: Entry functions cannot return values. Use view functions for read operations.
- **Excessive Computation**: Long-running computations cause high gas costs and potential timeouts.
- **Global Locks**: Accessing single global resources creates bottlenecks under Block-STM.
- **Unbounded Loops**: Loops over unbounded vectors risk gas exhaustion.

## Related Concepts

- **View Functions**: Read-only functions for querying state without gas costs
- **Script Functions**: Legacy transaction entry points, now superseded by entry functions
- **Public Functions**: Module functions callable by other modules but not directly via transactions
- **Inline Functions**: Private helper functions for code organization

---

## estimate_gas_price

# estimate_gas_price

## Overview

Retrieve current gas price estimates for Aptos transactions. This endpoint provides three tiers of gas pricing to help applications choose appropriate fees based on urgency: prioritized (fast), standard, and deprioritized (economy). Gas prices are measured in octas per gas unit (1 APT = 100,000,000 octas).

## Endpoint

`GET /v1/estimate_gas_price`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/estimate_gas_price" \
      -H "Accept: application/json"
```

## Response Fields

- `prioritized_gas_estimate` (`integer, required`): Higher gas price suggestion for urgent transactions
- `gas_estimate` (`integer, required`): Standard gas price suggestion for normal transaction flow
- `deprioritized_gas_estimate` (`integer, required`): Lower gas price suggestion for cost-sensitive, non-urgent transactions

## Successful Response

```json
{
  "prioritized_gas_estimate": 150,
  "gas_estimate": 100,
  "deprioritized_gas_estimate": 100
}
```

## Error Responses

### Error 1

- Code: `internal_error`
- Description: Server error estimating gas

### Error 2

- Code: `service_unavailable`
- Description: Gas estimation temporarily unavailable

## Code Examples

cURL
Python
TypeScript
Rust

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/estimate_gas_price" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
gas = client.estimate_gas_price()

print(f"Prioritized: {gas['prioritized_gas_estimate']} octas/unit")
print(f"Standard:    {gas['gas_estimate']} octas/unit")
print(f"Economy:     {gas['deprioritized_gas_estimate']} octas/unit")

# Use in transaction building
gas_unit_price = int(gas["gas_estimate"])
max_gas_amount = 2000  # typical for simple transfers
max_cost_octas = gas_unit_price * max_gas_amount
max_cost_apt = max_cost_octas / 100_000_000
print(f"Max transaction cost: {max_cost_apt} APT")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// The SDK handles gas estimation internally, but you can also query directly
const response = await fetch(
  "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/estimate_gas_price",
  { headers: { "Accept": "application/json" } }
);
const estimates = await response.json();
console.log(`Prioritized: ${estimates.prioritized_gas_estimate} octas`);
console.log(`Standard:    ${estimates.gas_estimate} octas`);
console.log(`Economy:     ${estimates.deprioritized_gas_estimate} octas`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let gas = client.estimate_gas_price().await?;

println!("Standard gas estimate: {} octas/unit", gas.inner().gas_estimate);
println!("Prioritized: {} octas/unit", gas.inner().prioritized_gas_estimate);
```

## Understanding Aptos Gas

Aptos gas works differently from EVM chains:

| Concept        | Description                                                                      |
| -------------- | -------------------------------------------------------------------------------- |
| Gas Unit Price | Price per unit of gas in octas (this endpoint provides estimates)                |
| Gas Used       | Actual gas units consumed during execution (varies by operation)                 |
| Max Gas Amount | Maximum gas units the sender is willing to pay (set during transaction building) |
| Total Cost     | `gas_used * gas_unit_price` in octas (charged after execution)                   |

**Typical gas usage by operation:**

| Operation                     | Approximate Gas Units |
| ----------------------------- | --------------------- |
| Simple APT transfer           | 10-20                 |
| Token transfer                | 20-50                 |
| Smart contract call (simple)  | 50-200                |
| Smart contract call (complex) | 200-5,000             |
| Module deployment             | 1,000-50,000          |

At 100 octas per gas unit, a simple APT transfer costs approximately 0.000002 APT (2,000 octas), making Aptos one of the most cost-effective layer-1 blockchains.

## Use Cases

1. **Dynamic Transaction Fee Setting**: Automatically adjust transaction gas prices based on current network conditions rather than using hardcoded values, ensuring reliable execution without overpaying.

2. **User Experience Optimization**: Offer users multiple speed/cost options (fast/normal/economy) by exposing the three gas tier estimates, similar to modern blockchain wallets.

3. **Batch Operation Cost Planning**: Estimate total costs for batch operations by multiplying gas estimates by expected gas consumption per transaction, helping optimize batching strategies.

4. **Gas Price Monitoring**: Track gas price trends over time to identify network congestion patterns and schedule non-urgent operations during low-fee periods.

5. **Multi-Chain Cost Comparison**: Compare Aptos gas costs with other blockchains to provide users with informed decisions about which chain to use for specific operations.

6. **Smart Contract Budgeting**: Applications with gas sponsorship or limited budgets can use deprioritized estimates to maximize transaction throughput within budget constraints.

## Best Practices

**Query Frequency**: Gas prices on Aptos are relatively stable compared to EVM chains due to the Block-STM parallel execution model. Query every 30-60 seconds for normal applications, or more frequently during known high-traffic events.

**Price Selection Logic**: For user-initiated transactions, use the standard estimate. For urgent operations like liquidations or time-sensitive trades, use prioritized. For background tasks or gas-sponsored operations, use deprioritized.

**Safety Margins**: Add a 10-20% buffer to estimates to account for price fluctuations between estimation and submission, especially for prioritized transactions during volatile periods.

**Transaction Expiration**: Set appropriate expiration timestamps (typically 60-120 seconds) that match your gas tier choice. Prioritized transactions should have shorter expirations, while deprioritized can be longer.

**Error Recovery**: If gas estimation fails (500/503 errors), fall back to conservative hardcoded values. For mainnet, safe fallback values are: prioritized=200, standard=100, deprioritized=100 octas per gas unit.

**Combining with Simulation**: For precise cost estimates, first call `estimate_gas_price` for the unit price, then simulate the transaction via `POST /v1/transactions/simulate` to get the actual gas units consumed. Multiply the two for the total cost.

## Performance Considerations

This endpoint is extremely lightweight and typically responds in under 20ms. The estimates are calculated based on recent block gas usage and mempool analysis, updated every block (approximately every 4 seconds on mainnet).

Gas estimates reflect the minimum gas unit price needed for inclusion, but actual transaction costs depend on gas consumption (`gas_used * gas_unit_price`). A simple transfer might consume 10-20 gas units, while complex smart contract interactions can consume thousands.

The response is under 200 bytes, making it suitable for frequent polling even on bandwidth-constrained connections.

## Related Endpoints

- `/v1/transactions/simulate` - Simulate a transaction to get exact gas usage
- `/v1/transactions` - Submit transactions using the estimated gas price
- `/v1` - Get ledger info (useful for setting expiration timestamps)
- `/v1/transactions/encode_submission` - Encode a transaction with gas parameters

---

## events

# events

## Overview

Events in Move provide a mechanism for smart contracts to emit structured notifications about state changes to off-chain systems. Events enable indexers, applications, and users to track on-chain activities without continuously polling resources.

## Technical Details

Events are defined as Move structs and emitted using the `event::emit` function:

```move
use aptos_framework::event;

struct TransferEvent has drop, store {
    from: address,
    to: address,
    amount: u64
}

public entry fun transfer_with_event(from: &signer, to: address, amount: u64) {
    // Transfer logic here
    event::emit(TransferEvent {
        from: signer::address_of(from),
        to,
        amount
    });
}
```

**Event Properties**:

- Stored off-chain in transaction metadata, not on-chain state
- Ordered sequentially within each transaction
- Identified by type and containing transaction
- Queryable via REST API and GraphQL indexer
- Support generic type parameters
- Require `drop` and `store` abilities

## How Events Work

When code emits an event during transaction execution:

1. Event data is serialized and attached to the transaction
2. Validators include events in the committed transaction output
3. Indexers process events and make them queryable
4. Applications query events via REST or GraphQL APIs
5. Events provide an audit trail of contract activity

Unlike state changes that modify resources, events create an append-only log that never changes.

## Practical Examples

**Token Transfer Events:**

```move
struct CoinTransferEvent has drop, store {
    sender: address,
    receiver: address,
    amount: u64,
    coin_type: TypeInfo
}
```

**NFT Marketplace Events:**

```move
struct ListingCreatedEvent has drop, store {
    seller: address,
    token_id: TokenId,
    price: u64,
    expiration: u64
}

struct PurchaseEvent has drop, store {
    buyer: address,
    seller: address,
    token_id: TokenId,
    price: u64
}
```

**DeFi Protocol Events:**

```move
struct SwapEvent has drop, store {
    user: address,
    coin_in_type: TypeInfo,
    coin_out_type: TypeInfo,
    amount_in: u64,
    amount_out: u64,
    fee: u64
}
```

## Use Cases

Events enable powerful off-chain functionality:

1. **User Activity Feeds**: Display transaction history and account activity in wallets and applications.

2. **Real-Time Notifications**: Trigger webhooks or push notifications when specific events occur.

3. **Analytics Dashboards**: Aggregate events to compute metrics like trading volume, user growth, or protocol usage.

4. **Compliance and Auditing**: Maintain immutable audit trails of all contract interactions for regulatory requirements.

5. **State Reconstruction**: Replay events to rebuild application state without querying all resources.

6. **Cross-Contract Coordination**: Monitor events from other contracts to trigger conditional logic.

## Best Practices

**Structured Event Design**: Design events with all information needed by consumers. Include addresses, amounts, timestamps, and type information.

**Consistent Naming**: Use descriptive event names with consistent patterns: `TransferEvent`, `MintEvent`, `BurnEvent`.

**Emit After Success**: Only emit events after operations succeed to avoid confusing off-chain systems with failed attempt notifications.

**Version Events**: When upgrading contracts, consider versioning events or adding optional fields to maintain backward compatibility with indexers.

**Gas Efficiency**: Events are relatively cheap but not free. Emit only essential notifications.

**Type Information**: Include type parameters or TypeInfo when events involve generic types to enable proper deserialization.

## Event Querying

Access events via multiple endpoints:

**REST API by Creation Number**:

```
GET /v1/accounts/{address}/events/{creation_number}
```

**REST API by Handle**:

```
GET /v1/accounts/{address}/events/{event_handle}/{field_name}
```

**GraphQL Indexer** (when available): Provides complex filtering, aggregation, and joins across multiple event types.

## Common Patterns

**Event Handles** (Legacy Pattern):

```move
struct EventStore has key {
    transfer_events: EventHandle<TransferEvent>
}
```

**Modern Event Emission** (Recommended):

```move
// No event handle needed
public entry fun transfer(from: &signer, to: address, amount: u64) {
    // Logic
    event::emit(TransferEvent { from: signer::address_of(from), to, amount });
}
```

The modern approach is simpler and more efficient.

## Related Concepts

- **Event Handles**: Legacy mechanism for organizing events (still supported but not recommended for new code)
- **GraphQL Indexer**: Query engine for complex event analysis
- **REST Event Endpoints**: Direct event access via REST API
- **Transaction Metadata**: Events are stored with transaction results

---

## events_by_creation_number

# events_by_creation_number

## Overview

Retrieve events from a specific event stream identified by an account address and creation number. Each event handle in Aptos has a unique creation number assigned when the handle is created. This endpoint lets you query, paginate, and monitor individual event streams for specific on-chain activities like token transfers, staking rewards, or governance votes.

> **Operational note:** This route also relies on indexer-backed event readers on shared Dwellir Aptos fullnodes. If the backend responds with `internal_error` because the indexer reader is unavailable, use GraphQL or the owning resource as your fallback source of truth.

## Endpoint

`GET /v1/accounts/{address}/events/{creation_number}`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# Get the first 10 events from creation number 2 (deposit events for 0x1)
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/2?limit=10" \
      -H "Accept: application/json"

    # Paginate from a specific sequence number
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/2?start=100&limit=100" \
      -H "Accept: application/json"
```

## Response Fields

- `version` (`string, required`): Ledger version when the event was emitted
- `guid.creation_number` (`string, required`): The creation number identifying this event stream
- `guid.account_address` (`string, required`): The account that owns this event stream
- `sequence_number` (`string, required`): Sequential number within this event stream (starts at 0)
- `type` (`string, required`): Fully qualified Move type of the event payload
- `data` (`object, required`): The structured event payload, fields vary by event type

## Successful Response

```json
[
  {
    "version": "123456789",
    "guid": {
      "creation_number": "2",
      "account_address": "0x1"
    },
    "sequence_number": "42",
    "type": "0x1::coin::DepositEvent",
    "data": {
      "amount": "1000000"
    }
  },
  {
    "version": "123456800",
    "guid": {
      "creation_number": "2",
      "account_address": "0x1"
    },
    "sequence_number": "43",
    "type": "0x1::coin::DepositEvent",
    "data": {
      "amount": "500000"
    }
  }
]
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid address or creation number format

### Error 2

- Code: `resource_not_found`
- Description: Event stream does not exist at this creation number

### Error 3

- Code: `account_not_found`
- Description: Account does not exist

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Get the first 10 events from creation number 2 (deposit events for 0x1)
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/2?limit=10" \
  -H "Accept: application/json"

# Paginate from a specific sequence number
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/2?start=100&limit=100" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Fetch recent events
events = client.account_events_by_creation_number("0x1", "2", start=None, limit=10)
for event in events:
    print(f"Seq {event['sequence_number']}: {event['type']} at version {event['version']}")
    print(f"  Data: {event['data']}")

# Paginate through all events
def stream_events(client, address, creation_number, start_seq=0):
    seq = start_seq
    while True:
        batch = client.account_events_by_creation_number(
            address, str(creation_number), start=str(seq), limit=100
        )
        if not batch:
            break
        for event in batch:
            yield event
        seq = int(batch[-1]["sequence_number"]) + 1

for event in stream_events(client, "0x1", 2):
    process_event(event)
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Fetch events via raw REST call
const response = await fetch(
  "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/2?limit=10",
  { headers: { "Accept": "application/json" } }
);
const events = await response.json();

for (const event of events) {
  console.log(`Event: ${event.type}, Amount: ${event.data.amount}`);
}
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let events = client
    .get_account_events("0x1", "2", Some(0), Some(10))
    .await?;
for event in events.inner() {
    println!("Seq {}: {} at version {}",
        event.sequence_number, event.typ, event.version);
}
```

## Discovering Creation Numbers

Creation numbers are assigned sequentially when event handles are created in an account's resources. To find the creation number for a specific event stream:

1. **Query the resource** that contains the event handle:
   ```bash
   GET /v1/accounts/{address}/resource/0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>
   ```

2. **Find the event handle** in the response:
   ```json
   {
     "data": {
       "deposit_events": {
         "counter": "42",
         "guid": {
           "id": {
             "addr": "0x1",
             "creation_num": "2"
           }
         }
       },
       "withdraw_events": {
         "counter": "15",
         "guid": {
           "id": {
             "addr": "0x1",
             "creation_num": "3"
           }
         }
       }
     }
   }
   ```

3. **Use the creation\_num** value (e.g., `2` for deposits, `3` for withdrawals) with this endpoint.

Common creation numbers for standard accounts:

- `0` - Account creation event
- `1` - Key rotation event
- `2` - CoinStore deposit events (for the first registered coin)
- `3` - CoinStore withdraw events (for the first registered coin)

## Use Cases

1. **Transfer Monitoring**: Track all deposits and withdrawals for an account by monitoring creation numbers 2 and 3 (for CoinStore events), enabling real-time balance tracking and notifications.

2. **Event Stream Processing**: Build event-driven architectures that react to specific on-chain events, such as triggering webhooks when a deposit exceeds a threshold.

3. **Historical Event Analysis**: Paginate through historical events to analyze patterns, aggregate statistics (total volume, transaction counts), or reconstruct state changes over time.

4. **Audit Trail Construction**: Build comprehensive audit logs by retrieving all events emitted by critical accounts (treasury, governance, multisig) in chronological order.

5. **Notification Systems**: Poll for new events at regular intervals and trigger alerts (email, Slack, push notifications) when specific event types or amounts are detected.

6. **DeFi Position Tracking**: Monitor staking events, reward distributions, and liquidity pool events to maintain accurate portfolio tracking.

## Best Practices

**Creation Number Discovery**: Always discover creation numbers dynamically from the containing resource rather than hardcoding them. While standard CoinStore uses 2/3 for deposit/withdraw, custom modules may use different numbers.

**Pagination Strategy**: Use `limit=100` to minimize API calls for bulk historical retrieval. Use the last event's `sequence_number + 1` as the next `start` parameter.

**Sequence Number Properties**: Sequence numbers within a stream are guaranteed to be sequential with no gaps, starting from 0. Missing sequence numbers indicate data synchronization issues.

**Version Ordering**: While sequence numbers order events within a single stream, use the `version` field to establish ordering across different event streams or accounts.

**Legacy vs Modern Events**: This endpoint supports both the legacy event handle system and newer event handles. For new contracts using `0x1::event::emit()`, events may be queried differently through the indexer.

**Rate Limiting**: Avoid polling too frequently. For near-real-time monitoring, poll every 4-5 seconds (matching Aptos block time). For less urgent monitoring, poll every 30-60 seconds.

**Counter Check**: The `counter` field in the event handle resource tells you the total number of events emitted. Use this to determine if there are new events without fetching the events themselves.

## Performance Considerations

Event queries are efficient for reasonable ranges. Fetching 100 events typically completes in 50-100ms. Requesting the maximum of 100 events with complex payloads can take 100-200ms.

Events are stored in chronological order, making forward pagination (increasing sequence numbers) the most efficient access pattern. The creation number index provides O(1) lookup to the event stream root.

For applications requiring comprehensive event analysis across multiple accounts or event types, the GraphQL indexer provides more efficient bulk querying capabilities with complex filtering, aggregation, and cross-account event correlation.

## Related Endpoints

- `/v1/accounts/{address}/events/{event_handle}/{field_name}` - Query events by handle and field name
- `/v1/accounts/{address}/resource/{type}` - Discover event handles and creation numbers
- `/v1/accounts/{address}/resources` - List all resources to find event handles
- `/v1/transactions/by_version/{version}` - Get the transaction that emitted a specific event

---

## events_by_handle

# events_by_handle

## Endpoint

`GET /v1/accounts/{address}/events/{event_handle}/{field_name}`

## Request

### Path Parameters

| Name          | Type   | Required | Description                          |
| ------------- | ------ | -------- | ------------------------------------ |
| address       | string | Yes      | Account address                      |
| event\_handle | string | Yes      | Struct type with events              |
| field\_name   | string | Yes      | Field in the struct producing events |

### Query Parameters

| Name  | Type    | Required | Description       |
| ----- | ------- | -------- | ----------------- |
| start | string  | No       | Starting sequence |
| limit | integer | No       | Page size         |

## Response

### Success Response (200)

Returns an array of event objects from the specified handle:

```json
[
  {
    "version": "123456789",
    "guid": {
      "creation_number": "3",
      "account_address": "0x1"
    },
    "sequence_number": "5",
    "type": "0x1::account::CoinRegisterEvent",
    "data": {
      "type_info": {
        "account_address": "0x1",
        "module_name": "aptos_coin",
        "struct_name": "AptosCoin"
      }
    }
  }
]
```

### Error Responses

| Status | Error Code           | Description                                   |
| ------ | -------------------- | --------------------------------------------- |
| 400    | invalid\_input       | Invalid address, handle, or field name format |
| 404    | resource\_not\_found | Event handle or field doesn't exist           |
| 404    | account\_not\_found  | Account doesn't exist                         |

## Code Examples

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/accounts/0x1/events/0x1::account::Account/coin_register_events?limit=5" \
  -H "Accept: application/json"
```

Python example:

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
events = client.account_events_by_event_handle(
    "0x1",
    "0x1::account::Account",
    "coin_register_events",
    limit=5
)
```

TypeScript example:

```typescript
const events = await aptos.getEventsByEventHandle({
  accountAddress: "0x1",
  eventHandleStruct: "0x1::account::Account",
  fieldName: "coin_register_events",
  minimumLedgerVersion: 0,
  options: { limit: 5 }
});
```

## Use Cases

This endpoint enables powerful event-driven architecture patterns:

1. **Contract Event Monitoring**: Monitor specific event types emitted by smart contracts, such as tracking all token transfer events or NFT mint events from a marketplace contract.

2. **User Activity Tracking**: Build user activity feeds by querying events from user-controlled resources, showing transaction history and state changes.

3. **Cross-Contract Analytics**: Aggregate events across multiple contracts or accounts to analyze ecosystem-wide trends, such as total trading volume or user adoption metrics.

4. **Real-Time Notifications**: Poll event handles to detect new events and trigger notifications, webhooks, or automated responses for critical contract activities.

5. **State Verification**: Verify that expected state changes occurred by checking for corresponding events, useful for transaction confirmation UIs.

6. **Debugging and Testing**: During development, inspect event emissions to validate contract behavior and ensure events are emitted with correct data.

## Best Practices

**Event Handle Structure**: The event handle path follows the pattern `address::module::Struct/field_name`. The struct must contain an `EventHandle<T>` field with the specified name.

**Type Information**: Always inspect the returned `type` field to understand the event payload structure. Event types are strongly typed in Move and the type string indicates the exact Move struct emitted.

**Pagination**: For event streams with many events, use pagination with the `start` parameter set to the last seen `sequence_number + 1` to avoid missing events or duplicates.

**Polling Frequency**: Poll at reasonable intervals (5-30 seconds) based on your use case. Too frequent polling wastes resources, while too infrequent polling may delay notifications.

**Handle Discovery**: Find available event handles by querying account resources and inspecting struct fields of type `EventHandle<T>`. The ABI from module queries shows event handle field names.

## Performance Considerations

Event queries by handle are optimized with indexed lookups, typically completing in 50-150ms for reasonable limits. Response times increase with larger limit values and complex event payload structures.

This method is more efficient than querying by creation number when you know the specific contract and field name, as it avoids resource enumeration overhead. However, for bulk event analysis across many handles, consider using the GraphQL indexer for better query flexibility and performance.

---

## formal_verification

# formal_verification

Formal verification in Move uses the Move Prover tool to mathematically verify that smart contracts behave correctly under all possible conditions. Unlike traditional testing which checks specific scenarios, formal verification provides mathematical proofs that your code satisfies specified properties for all possible inputs and states.

## Overview

The Move Prover is a sophisticated verification tool that analyzes Move modules and proves that they satisfy formal specifications written in the Move Specification Language (MSL). It uses automated theorem proving techniques to verify safety properties, invariants, and functional correctness of your smart contracts before deployment.

## Technical Implementation

Formal verification works by translating Move code and specifications into logical formulas that can be processed by SMT (Satisfiability Modulo Theories) solvers. The prover checks whether your code can violate any specified invariants or preconditions under any execution path.

### Specification Syntax

```move
module 0x1::verified_coin {
    use std::signer;

    struct Balance has key {
        value: u64
    }

    spec Balance {
        invariant value <= MAX_U64;
    }

    public fun transfer(from: &signer, to: address, amount: u64) acquires Balance {
        let from_addr = signer::address_of(from);
        let from_balance = &mut borrow_global_mut<Balance>(from_addr).value;
        let to_balance = &mut borrow_global_mut<Balance>(to).value;

        *from_balance = *from_balance - amount;
        *to_balance = *to_balance + amount;
    }

    spec transfer {
        requires exists<Balance>(signer::address_of(from));
        requires exists<Balance>(to);
        requires borrow_global<Balance>(signer::address_of(from)).value >= amount;
        ensures borrow_global<Balance>(to).value == old(borrow_global<Balance>(to).value) + amount;
        ensures borrow_global<Balance>(signer::address_of(from)).value ==
                old(borrow_global<Balance>(signer::address_of(from)).value) - amount;
    }
}
```

## Real-World Use Cases

1. **DeFi Protocol Security**: Verify that token supply invariants hold across all transfer, mint, and burn operations, ensuring no tokens can be created or destroyed unexpectedly.

2. **Access Control Validation**: Prove that administrative functions can only be called by authorized accounts and that privilege escalation is impossible.

3. **Asset Conservation**: Verify that total value in lending protocols, DEXes, or vaults remains constant during operations, preventing loss of funds.

4. **Upgrade Safety**: Prove that module upgrades maintain backward compatibility and preserve critical invariants about stored data.

5. **Overflow Protection**: Mathematically verify that arithmetic operations cannot overflow or underflow, eliminating a major class of vulnerabilities.

6. **State Machine Correctness**: Verify that state transitions in governance systems, auctions, or multi-step processes follow valid sequences.

## Best Practices

**Focus on Critical Properties**: Start by verifying the most important safety properties such as asset conservation, access control, and state invariants. Don't try to verify everything at once.

**Use Modular Specifications**: Break down complex specifications into smaller, reusable components using schema and helper functions in MSL.

**Test Specifications**: Specifications can have bugs too. Use the prover's counterexample feature to validate that your specifications catch actual bugs.

**Iterative Refinement**: Begin with simple invariants and progressively add more detailed specifications as you gain confidence in the prover's behavior.

**Performance Optimization**: Use timeouts, specify verification scope, and leverage modular verification to keep proof times manageable for large codebases.

**Documentation**: Specifications serve as precise documentation of your contract's behavior. Keep them readable and well-commented.

## Running the Move Prover

```bash
# Verify all modules in a package
aptos move prove --package-dir .

# Verify specific module
aptos move prove --package-dir . --filter "0x1::verified_coin"

# Generate detailed error traces
aptos move prove --package-dir . --verbose

# Set timeout for proofs
aptos move prove --package-dir . --timeout 120
```

The prover will report any specification violations with counterexamples showing how the property can be violated, or confirm that all specifications hold.

## Related Concepts

- [Testing](https://www.dwellir.com/docs/aptos/testing) - Complement formal verification with comprehensive unit tests
- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Organize code to facilitate verification
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Understand resource semantics for writing correct specifications

---

## fungible_assets

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# fungible_assets

Fungible Assets (FA) represent the next generation token standard on Aptos, providing enhanced functionality compared to the legacy Coin standard. The GraphQL API enables comprehensive querying of FA balances, metadata, transfer histories, and analytics, essential for wallets, DEXes, portfolio trackers, and any application working with tokens.

## Overview

The Fungible Asset standard improves upon Coins by supporting programmable behaviors, better metadata management, and composability with the Object model. GraphQL queries provide indexed access to all FA data including current balances, historical activities, supply metrics, and token metadata across all fungible assets on Aptos.

## Core Queries

### Account Balances

```graphql
query FaBalances($owner: String!) {
  current_fungible_asset_balances(
    where: { owner_address: { _eq: $owner } }
  ) {
    owner_address
    asset_type
    amount
    last_transaction_version
    metadata {
      name
      symbol
      decimals
      icon_uri
      project_uri
    }
  }
}
```

### Token Metadata

```graphql
query TokenInfo($asset_type: String!) {
  fungible_asset_metadata(
    where: { asset_type: { _eq: $asset_type } }
  ) {
    asset_type
    creator_address
    name
    symbol
    decimals
    icon_uri
    project_uri
    supply_aggregator_table_handle
    supply_aggregator_table_key
  }
}
```

### Transfer History

```graphql
query TransferHistory($owner: String!, $limit: Int!) {
  fungible_asset_activities(
    where: {
      _or: [
        { owner_address: { _eq: $owner } },
        { to_address: { _eq: $owner } }
      ]
    },
    order_by: { transaction_version: desc },
    limit: $limit
  ) {
    transaction_version
    owner_address
    to_address
    amount
    type
    asset_type
    transaction_timestamp
  }
}
```

### Supply Metrics

```graphql
query SupplyMetrics($asset_type: String!) {
  fungible_asset_supply(
    where: { asset_type: { _eq: $asset_type } }
  ) {
    asset_type
    current_supply
    max_supply
    total_minted
    total_burned
  }
}
```

### Top Holders

```graphql
query TopHolders($asset_type: String!, $limit: Int!) {
  current_fungible_asset_balances(
    where: {
      asset_type: { _eq: $asset_type },
      amount: { _gt: "0" }
    },
    order_by: { amount: desc },
    limit: $limit
  ) {
    owner_address
    amount
    last_transaction_version
  }
}
```

## Real-World Use Cases

1. **Wallet Applications**: Display comprehensive token portfolios with balances, prices, and metadata for all FAs held by users across the Aptos ecosystem.

2. **DEX Interfaces**: Query token metadata, verify decimals, fetch logos, and track liquidity for trading pairs on decentralized exchanges.

3. **Portfolio Trackers**: Aggregate holdings across multiple wallets, calculate total values, and track historical balance changes over time.

4. **Token Analytics**: Analyze holder distribution, supply metrics, transfer volumes, and other statistics for tokens and liquidity pools.

5. **Payment Systems**: Verify balances before transactions, fetch current exchange rates, and track payment histories for accounting purposes.

6. **DeFi Dashboards**: Monitor staking positions, lending collateral, farming rewards, and other DeFi activities involving fungible assets.

## Best Practices

**Cache Metadata**: Token metadata (name, symbol, decimals, logo) changes rarely - cache it with long TTLs to reduce API calls.

**Handle Decimals**: Always account for token decimals when displaying amounts - a balance of 1000000 with 6 decimals is actually 1.0 tokens.

**Batch Balance Queries**: Fetch all balances for an address in a single query rather than separate requests per token.

**Track Versions**: Use transaction\_version to detect balance changes and avoid displaying stale data.

**Filter Zero Balances**: Exclude zero-balance entries to avoid cluttering portfolio displays with tokens users no longer hold.

**Use Aggregations**: For analytics, use aggregate queries to compute statistics efficiently rather than fetching all records.

**Pagination**: Implement proper pagination for transfer histories and holder lists to handle large datasets.

## TypeScript Integration

```typescript
import { ApolloClient, gql } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql"
});

interface TokenBalance {
  asset_type: string;
  amount: string;
  decimals: number;
  symbol: string;
  name: string;
}

async function getPortfolio(address: string): Promise<TokenBalance[]> {
  const { data } = await client.query({
    query: gql`
      query Portfolio($address: String!) {
        current_fungible_asset_balances(
          where: {
            owner_address: { _eq: $address },
            amount: { _gt: "0" }
          }
        ) {
          asset_type
          amount
          metadata {
            name
            symbol
            decimals
          }
        }
      }
    `,
    variables: { address }
  });

  return data.current_fungible_asset_balances.map((balance: any) => ({
    asset_type: balance.asset_type,
    amount: balance.amount,
    decimals: balance.metadata.decimals,
    symbol: balance.metadata.symbol,
    name: balance.metadata.name
  }));
}

function formatTokenAmount(amount: string, decimals: number): string {
  const value = parseInt(amount) / Math.pow(10, decimals);
  return value.toLocaleString(undefined, {
    minimumFractionDigits: 2,
    maximumFractionDigits: decimals
  });
}
```

## Advanced Queries

### Portfolio Value Tracking

```graphql
query PortfolioWithPrices($owner: String!) {
  balances: current_fungible_asset_balances(
    where: {
      owner_address: { _eq: $owner },
      amount: { _gt: "0" }
    }
  ) {
    asset_type
    amount
    metadata {
      name
      symbol
      decimals
      icon_uri
    }
  }
}
```

### Token Distribution Analysis

```graphql
query HolderDistribution($asset_type: String!) {
  total_holders: current_fungible_asset_balances_aggregate(
    where: {
      asset_type: { _eq: $asset_type },
      amount: { _gt: "0" }
    }
  ) {
    aggregate { count }
  }

  large_holders: current_fungible_asset_balances_aggregate(
    where: {
      asset_type: { _eq: $asset_type },
      amount: { _gt: "1000000000" }
    }
  ) {
    aggregate { count }
  }

  total_supply: fungible_asset_supply(
    where: { asset_type: { _eq: $asset_type } }
  ) {
    current_supply
  }
}
```

### Activity Feed

```graphql
query RecentActivities($owner: String!, $limit: Int!) {
  fungible_asset_activities(
    where: {
      _or: [
        { owner_address: { _eq: $owner } },
        { to_address: { _eq: $owner } }
      ]
    },
    order_by: { transaction_version: desc },
    limit: $limit
  ) {
    transaction_version
    owner_address
    to_address
    amount
    type
    asset_type
    transaction_timestamp
    metadata {
      symbol
      decimals
    }
  }
}
```

## Fungible Assets vs Coins

The Fungible Asset standard offers several improvements over the legacy Coin standard:

- **Object Integration**: FAs integrate with the Object model for better composability
- **Metadata Standards**: Built-in metadata support with URLs for icons and project info
- **Programmable Transfers**: Support for custom transfer logic and restrictions
- **Better Events**: More comprehensive event emissions for indexing
- **Future-Proof**: Designed for long-term extensibility

## Related Concepts

- [GraphQL Overview](https://www.dwellir.com/docs/aptos/graphql/overview) - GraphQL indexer introduction
- [Token Activities](https://www.dwellir.com/docs/aptos/token_activities) - NFT and token transfers
- [Aggregations](https://www.dwellir.com/docs/aptos/aggregations) - Statistical queries
- [Object Model](https://www.dwellir.com/docs/aptos/object_model) - Object-based asset design

---

## GraphQL API Overview

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# GraphQL API Overview

The Aptos GraphQL indexer gives you indexed, filterable access to transaction history, token activity, account state, and aggregate analytics without having to stitch together multiple REST requests yourself. Once GraphQL access is enabled for your API key, use `https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql` for mainnet queries.

## When to Use GraphQL

GraphQL is the better fit when you need:

- multi-table reads such as balances plus token metadata in one request
- server-side filtering, ordering, and pagination for dashboards or explorers
- aggregate queries for counts, sums, and activity rollups
- a predictable response shape driven by the exact fields your UI needs

For canonical ledger reads, transaction submission, or account-specific REST workflows, use the Aptos REST endpoints. For indexed history and analytics, GraphQL is usually the faster path.

## Request Shape

Every request is a standard GraphQL POST with a `query` string and optional `variables` object:

```json
{
  "query": "query LedgerInfo($limit: Int!) { ledger_infos(limit: $limit, order_by: { version: desc }) { chain_id version timestamp } }",
  "variables": {
    "limit": 1
  }
}
```

That lets you keep queries reusable and move environment-specific inputs, such as account addresses or time windows, into variables instead of string interpolation.

## Starter Query

```graphql
query LedgerInfo($limit: Int!) {
  ledger_infos(limit: $limit, order_by: { version: desc }) {
    chain_id
    version
    timestamp
  }
}
```

```bash
curl -X POST https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query LedgerInfo($limit: Int!) { ledger_infos(limit: $limit, order_by: { version: desc }) { chain_id version timestamp } }",
    "variables": { "limit": 1 }
  }'
```

This is a good health check because it exercises filtering, ordering, and the latest indexed ledger state in one small response.

## Common Query Patterns

Most production GraphQL usage falls into a few repeatable patterns:

- **lookup by address or asset type** with `where` filters
- **latest-first activity feeds** with `order_by` and `limit`
- **portfolio views** that fetch balances together with related metadata
- **analytics panels** that use `_aggregate` tables for counts and rollups

Start with a narrow filter and a low `limit`, then expand once you know the table and relation shape you need.

## Operational Notes

- GraphQL access is enabled per account. If the endpoint is not provisioned for your key yet, request enablement before building against it.
- Prefer variables over string-built queries so clients stay type-safe and easier to cache.
- Ask only for the fields you render. Smaller selection sets reduce payload size and make caching more effective.
- Add `order_by` explicitly for paged queries so your client does not rely on implicit ordering.
- Use aggregate tables for metrics and dashboards rather than fetching large result sets just to count them client-side.

## Related Guides

- [User Transactions](https://www.dwellir.com/docs/aptos/user_transactions) for indexed transaction history
- [Fungible Assets](https://www.dwellir.com/docs/aptos/fungible_assets) for balances and token metadata
- [Aggregations](https://www.dwellir.com/docs/aptos/aggregations) for counts and rollups
- [Subscriptions](https://www.dwellir.com/docs/aptos/subscriptions) for real-time GraphQL updates

---

## healthy

# healthy

## Overview

Check the health and availability of the Aptos REST API node. This endpoint returns HTTP 200 when the node is operational and synced, making it ideal for monitoring, load balancing, and automated health checks in production environments.

## Endpoint

`GET /v1/-/healthy`

## Request

### Path Parameters

None.

### Query Parameters

None.

### Request Body

None.

## Response

### Success Response (200)

Returns HTTP 200 with an empty or minimal response body when the node is healthy and ready to serve requests. The node is considered healthy when:

- The REST API service is running and responsive
- The underlying node is syncing or fully synced with the network
- Database connections are operational
- Critical system resources are available

### Error Responses

| Status | Error Code           | Description                                   |
| ------ | -------------------- | --------------------------------------------- |
| 503    | service\_unavailable | Node is unhealthy, not synced, or starting up |
| 500    | internal\_error      | Critical error preventing normal operation    |

Any non-200 response indicates the node should not receive production traffic.

## Code Examples

Shell script for monitoring:

```bash
curl -s -X GET -o /dev/null -w "%{http_code}\n" https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/-/healthy -H "Accept: application/json"
```

Python monitoring script:

```python
import requests

def is_node_healthy(api_key):
    try:
        response = requests.get(
            f"https://api-aptos-mainnet.n.dwellir.com/{api_key}/v1/-/healthy",
            timeout=5
        )
        return response.status_code == 200
    except requests.exceptions.RequestException:
        return False

if is_node_healthy("YOUR_API_KEY"):
    print("Node is healthy")
else:
    print("Node is unhealthy")
```

TypeScript health check:

```typescript
async function checkNodeHealth(apiKey: string): Promise<boolean> {
  try {
    const response = await fetch(
      `https://api-aptos-mainnet.n.dwellir.com/${apiKey}/v1/-/healthy`,
      { signal: AbortSignal.timeout(5000) }
    );
    return response.status === 200;
  } catch (error) {
    return false;
  }
}
```

Docker healthcheck in docker-compose.yml:

```yaml
services:
  app:
    healthcheck:
      test: ["CMD", "curl", "-f", "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/-/healthy"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
```

## Use Cases

This endpoint is essential for production infrastructure:

1. **Load Balancer Health Checks**: Configure load balancers (AWS ALB, nginx, HAProxy) to use this endpoint for backend health checks, automatically routing traffic away from unhealthy nodes.

2. **Kubernetes Readiness Probes**: Set up Kubernetes readiness and liveness probes to ensure pods are only marked ready when the Aptos node is fully synced and operational.

3. **Monitoring and Alerting**: Integrate with monitoring systems (Prometheus, Datadog, New Relic) to track node availability and trigger alerts when health checks fail.

4. **Automated Failover**: Build high-availability systems that automatically switch to backup API endpoints when the primary node becomes unhealthy.

5. **CI/CD Pipeline Gates**: Use health checks in deployment pipelines to verify services are operational before promoting to production or completing rolling updates.

6. **Service Discovery**: Register nodes with service discovery systems (Consul, etcd) and use health checks to maintain an accurate registry of available endpoints.

## Best Practices

**Timeout Configuration**: Set reasonable timeouts (3-5 seconds) for health check requests. If a node doesn't respond quickly, it's likely unhealthy and shouldn't serve traffic.

**Check Frequency**: Balance between responsiveness and load. For critical services, check every 10-30 seconds. For monitoring dashboards, 60 seconds is typically sufficient.

**Retry Logic**: Implement exponential backoff for retries. Don't immediately mark a node as unhealthy on a single failed check - wait for 2-3 consecutive failures.

**Circuit Breaker Pattern**: After detecting an unhealthy node, implement a circuit breaker that waits before rechecking. Avoid overwhelming an unhealthy node with continuous health checks.

**Multiple Nodes**: Never rely on a single node. Always maintain connections to multiple API endpoints and use health checks to select healthy ones dynamically.

**Logging**: Log health check failures with timestamps and status codes for troubleshooting and capacity planning.

## Performance Considerations

Health check requests are extremely lightweight and typically complete in under 20ms. They don't touch the blockchain state layer - the node simply reports its operational status.

However, health checks do consume API rate limits and network resources. Avoid checking more frequently than every 5-10 seconds to prevent unnecessary load on shared infrastructure.

For Dwellir's managed API infrastructure, health checks are already performed internally at the load balancer level. The health endpoint is exposed primarily for client-side monitoring and verification purposes.

## Integration with Other Endpoints

Combine health checks with the `/v1` (ledger\_info) endpoint for more detailed node status:

- `/v1/-/healthy` - Quick operational check (< 20ms)
- `/v1` - Detailed sync status with version information (50-100ms)

Use `/healthy` for rapid health verification, then query `/v1` for diagnostic information if health checks start failing.

---

## historical_replay

> Coming soon: Need support for this? Email <support@dwellir.com> if you want early access

# historical_replay

Historical replay enables replaying transactions from any point in blockchain history, essential for backfilling indexes, auditing past events, rebuilding state, and analyzing historical patterns. This feature provides efficient access to the entire transaction history without running full archive nodes.

## Overview

The streaming API supports replaying transactions from genesis or any specific version forward, delivering historical data with the same performance and reliability as real-time streams. This enables applications to build complete indexes from scratch, verify historical states, or analyze long-term trends efficiently.

## Starting from Specific Versions

```typescript
import { TransactionStreamClient } from "./generated/aptos_stream";

const client = new TransactionStreamClient(
  "stream.aptos.dwellir.com:443",
  credentials.createSsl()
);

// Replay from specific version
const request = {
  startingVersion: 100000000n, // Start from version 100M
  includeEvents: true,
  includeChanges: true
};

const stream = client.subscribe(request, metadata);

stream.on("data", (transaction) => {
  console.log(`Processing historical transaction: ${transaction.version}`);
  // Process transaction
});
```

## Genesis Replay

```typescript
// Replay from genesis (version 0)
const genesisRequest = {
  startingVersion: 0n,
  includeEvents: true,
  includeChanges: false  // Optimize bandwidth if changes not needed
};

const genesisStream = client.subscribe(genesisRequest, metadata);

let processedCount = 0;
const startTime = Date.now();

genesisStream.on("data", (transaction) => {
  processedCount++;

  if (processedCount % 10000 === 0) {
    const elapsed = (Date.now() - startTime) / 1000;
    const rate = processedCount / elapsed;
    console.log(`Processed ${processedCount} transactions (${rate.toFixed(0)} tx/s)`);
  }
});
```

## Real-World Use Cases

1. **Index Backfilling**: Build complete indexes from genesis when launching new applications or adding new index types to existing systems.

2. **Data Migration**: Migrate historical data from one database or schema to another by replaying transactions through updated processors.

3. **Historical Analysis**: Analyze long-term trends, patterns, and statistics by processing years of blockchain history efficiently.

4. **Audit and Compliance**: Replay specific time periods to audit transactions, verify compliance, or investigate historical events.

5. **State Reconstruction**: Rebuild application state from scratch by replaying all relevant transactions to verify correctness or recover from corruption.

6. **Research and Analytics**: Process complete blockchain history for academic research, market analysis, or protocol performance studies.

## Best Practices

**Checkpoint Regularly**: Save progress frequently during historical replay to enable resumption without reprocessing millions of transactions.

**Batch Processing**: Process transactions in batches and commit to database periodically rather than per-transaction to maximize throughput.

**Resource Management**: Monitor memory usage and implement buffering strategies to handle high-throughput replay without overwhelming systems.

**Progress Tracking**: Implement detailed progress tracking with estimated completion times to monitor long-running historical replays.

**Parallel Processing**: Split historical ranges across multiple workers to parallelize processing and reduce total replay time.

**Validate Completeness**: Track version gaps and verify continuous coverage to ensure no transactions are missed during replay.

**Optimize Queries**: Use efficient database bulk insert operations and disable unnecessary indexes during replay for maximum throughput.

## Batch Replay Implementation

```typescript
class HistoricalReplayProcessor {
  private batchSize: number = 1000;
  private buffer: Transaction[] = [];

  async replayRange(startVersion: bigint, endVersion: bigint) {
    const request = {
      startingVersion: startVersion,
      includeEvents: true
    };

    const stream = client.subscribe(request, metadata);

    stream.on("data", async (transaction) => {
      // Stop at end version
      if (transaction.version > endVersion) {
        stream.cancel();
        await this.flushBuffer();
        return;
      }

      this.buffer.push(transaction);

      // Process in batches
      if (this.buffer.length >= this.batchSize) {
        await this.processBatch(this.buffer);
        this.buffer = [];
      }
    });
  }

  private async processBatch(transactions: Transaction[]) {
    const records = transactions.map(tx => this.transformTransaction(tx));

    // Bulk insert
    await this.database.bulkInsert("transactions", records);

    // Update checkpoint
    const lastVersion = transactions[transactions.length - 1].version;
    await this.saveCheckpoint(lastVersion);
  }

  private async flushBuffer() {
    if (this.buffer.length > 0) {
      await this.processBatch(this.buffer);
    }
  }
}
```

## Progress Monitoring

```typescript
class ReplayMonitor {
  private startVersion: bigint;
  private endVersion: bigint;
  private currentVersion: bigint;
  private startTime: number;

  constructor(start: bigint, end: bigint) {
    this.startVersion = start;
    this.endVersion = end;
    this.currentVersion = start;
    this.startTime = Date.now();
  }

  update(version: bigint) {
    this.currentVersion = version;
  }

  getProgress(): ReplayProgress {
    const total = Number(this.endVersion - this.startVersion);
    const processed = Number(this.currentVersion - this.startVersion);
    const percent = (processed / total) * 100;

    const elapsed = Date.now() - this.startTime;
    const rate = processed / (elapsed / 1000);
    const remaining = (total - processed) / rate;

    return {
      percent: percent.toFixed(2),
      processed,
      total,
      rate: rate.toFixed(0),
      eta: new Date(Date.now() + remaining * 1000).toISOString()
    };
  }
}
```

## Parallel Replay Strategy

```typescript
async function parallelReplay(
  startVersion: bigint,
  endVersion: bigint,
  workerCount: number
) {
  const totalRange = endVersion - startVersion;
  const rangePerWorker = totalRange / BigInt(workerCount);

  const workers = [];

  for (let i = 0; i < workerCount; i++) {
    const workerStart = startVersion + (rangePerWorker * BigInt(i));
    const workerEnd = i === workerCount - 1
      ? endVersion
      : workerStart + rangePerWorker;

    workers.push(replayRange(workerStart, workerEnd, i));
  }

  await Promise.all(workers);
}

async function replayRange(
  start: bigint,
  end: bigint,
  workerId: number
) {
  console.log(`Worker ${workerId}: Replaying ${start} to ${end}`);

  const processor = new HistoricalReplayProcessor();
  await processor.replayRange(start, end);

  console.log(`Worker ${workerId}: Complete`);
}
```

## Recovery and Resumption

```typescript
class ReplayState {
  async saveCheckpoint(version: bigint, workerId: string) {
    await db.upsert("replay_checkpoints", {
      worker_id: workerId,
      last_version: version.toString(),
      updated_at: new Date()
    });
  }

  async loadCheckpoint(workerId: string): Promise<bigint | null> {
    const checkpoint = await db.findOne("replay_checkpoints", {
      worker_id: workerId
    });

    return checkpoint ? BigInt(checkpoint.last_version) : null;
  }

  async resumeReplay(startVersion: bigint, endVersion: bigint, workerId: string) {
    // Try to resume from last checkpoint
    const checkpoint = await this.loadCheckpoint(workerId);
    const resumeFrom = checkpoint || startVersion;

    console.log(`Resuming from version ${resumeFrom}`);

    return this.replayRange(resumeFrom, endVersion);
  }
}
```

## Performance Optimization

- **Disable Indexes**: Drop or disable indexes during bulk replay, rebuild after completion
- **Batch Commits**: Commit database transactions in large batches (10k-100k records)
- **Skip Validation**: Disable expensive validations during replay since data is already validated
- **Compression**: Use compression for network transfer to reduce bandwidth usage
- **SSD Storage**: Use fast SSDs for database writes during high-throughput replay

## Related Concepts

- [Streaming Overview](https://www.dwellir.com/docs/aptos/streaming/overview) - Introduction to streaming
- [Real-Time Streaming](https://www.dwellir.com/docs/aptos/real_time) - Processing current transactions
- [Custom Processors](https://www.dwellir.com/docs/aptos/custom_processors) - Building replay processors
- [Authentication](https://www.dwellir.com/docs/aptos/authentication) - Securing replay streams

---

## key_rotation

# key_rotation

Key rotation on Aptos allows users to change their account's authentication key while preserving the account address, enabling recovery from compromised keys, upgrading security schemes, and transitioning to different authentication methods without losing assets or on-chain identity.

## Overview

Unlike traditional blockchains where accounts are permanently tied to a single private key, Aptos separates account addresses from authentication keys. This architecture enables users to rotate their keys for security purposes while maintaining the same address. Key rotation supports both proven rotations (where you have the old key) and unproven rotations (using social recovery or multi-sig authorization).

## How Key Rotation Works

Every Aptos account has two components: the permanent address derived from the initial public key, and a mutable authentication key that controls access. When you rotate keys, only the authentication key changes while the address remains constant.

```move
// Using Aptos CLI for key rotation
aptos account rotate-key \
  --new-private-key-file ~/.aptos/new_key.key \
  --profile mainnet

// Programmatic key rotation
module 0x1::key_manager {
    use std::signer;
    use aptos_framework::account;

    public entry fun rotate_authentication_key(
        account: &signer,
        new_auth_key: vector<u8>
    ) {
        account::rotate_authentication_key(account, new_auth_key);
    }
}
```

## Proven Rotation

Proven rotation requires the current private key to authorize the key change:

```typescript
import { Aptos, Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";

const aptos = new Aptos();

// Current account (old key)
const oldPrivateKey = new Ed25519PrivateKey("0x...");
const account = Account.fromPrivateKey({ privateKey: oldPrivateKey });

// Generate new key
const newPrivateKey = Ed25519PrivateKey.generate();
const newAuthKey = newPrivateKey.publicKey().authKey();

// Rotate to new key
const transaction = await aptos.rotateAuthKey({
  sender: account,
  newAuthKey: newAuthKey
});

console.log(`Key rotated: ${transaction.hash}`);
```

## Unproven Rotation

Unproven rotation allows key changes through alternative authorization mechanisms like multi-sig or social recovery when the original key is lost:

```move
module 0x1::recovery {
    use std::signer;
    use aptos_framework::account;

    struct RecoveryConfig has key {
        guardians: vector<address>,
        threshold: u64
    }

    public entry fun setup_recovery(
        account: &signer,
        guardians: vector<address>,
        threshold: u64
    ) {
        move_to(account, RecoveryConfig { guardians, threshold });
    }

    public entry fun recover_account(
        recovered_addr: address,
        new_auth_key: vector<u8>,
        guardian_signatures: vector<vector<u8>>
    ) acquires RecoveryConfig {
        let config = borrow_global<RecoveryConfig>(recovered_addr);
        // Verify threshold of guardian signatures
        // Then rotate to new key
        // account::rotate_authentication_key_call(recovered_addr, new_auth_key);
    }
}
```

## Real-World Use Cases

1. **Security Incidents**: Immediately rotate keys when you suspect a private key has been compromised, protecting assets before an attacker can act.

2. **Hardware Wallet Migration**: Transition from a software wallet to a hardware wallet for enhanced security without changing your on-chain address.

3. **Social Recovery**: Implement social recovery systems where trusted friends or family can help recover accounts if keys are lost.

4. **Corporate Key Management**: Rotate employee access keys when staff changes while maintaining consistent corporate account addresses.

5. **Multi-Sig Evolution**: Upgrade from single-key control to multi-sig authorization as account value or importance grows.

6. **Custody Transitions**: Transfer custody of accounts between different custodians or security providers without asset transfers.

## Best Practices

**Verify New Keys**: Always verify you can sign with new keys before finalizing rotation. Test on testnet first for critical accounts.

**Secure Key Generation**: Generate new keys using cryptographically secure random number generators in isolated environments.

**Backup Immediately**: Back up new private keys in multiple secure locations before completing rotation.

**Document Rotation**: Maintain records of key rotations including dates, reasons, and new key fingerprints for audit purposes.

**Test Recovery Procedures**: Regularly test your recovery mechanisms to ensure they work before an emergency.

**Use Time Locks**: Consider implementing time-delayed rotations for high-value accounts to allow cancellation if unauthorized.

**Monitor After Rotation**: Watch account activity closely after rotation to detect any unauthorized access attempts.

## Key Rotation with Multi-Sig

Combine key rotation with multi-sig for enhanced security:

```move
module 0x1::multisig_rotation {
    use aptos_framework::account;
    use aptos_framework::multisig_account;

    public entry fun propose_key_rotation(
        proposer: &signer,
        multisig_address: address,
        new_auth_key: vector<u8>
    ) {
        // Create proposal to rotate multisig account key
        // Requires threshold approval from signers
    }

    public entry fun approve_key_rotation(
        approver: &signer,
        multisig_address: address,
        proposal_id: u64
    ) {
        // Approve the key rotation proposal
    }
}
```

## Security Considerations

**Old Key Compromise**: After rotation, the old private key can no longer control the account, but it could still be used to attempt social engineering attacks.

**Rotation Timing**: Complete rotations quickly once initiated to minimize the window where multiple keys could theoretically control the account.

**Authentication vs Address**: Remember that rotating the authentication key doesn't change the account address, so on-chain references remain valid.

**Transaction Replay**: Signed but unsubmitted transactions from before rotation will fail after the key changes.

**Emergency Contacts**: Maintain emergency contact information with trusted parties who can assist with recovery if needed.

## CLI Commands

```bash
# Generate new key pair
aptos key generate --output-file ~/.aptos/new_key.key

# Rotate to new key
aptos account rotate-key \
  --new-private-key-file ~/.aptos/new_key.key \
  --profile mainnet

# Verify rotation
aptos account lookup-address \
  --profile mainnet

# Test signing with new key
aptos move run \
  --function-id 0x1::aptos_account::test_transaction \
  --profile mainnet
```

## Related Concepts

- [Multi-Agent Transactions](https://www.dwellir.com/docs/aptos/multi_agent) - Coordinate multiple signers
- [Resource Accounts](https://www.dwellir.com/docs/aptos/resource_accounts) - Accounts without private keys
- [Authentication](https://www.dwellir.com/docs/aptos/authentication) - Authentication methods
- [Sponsored Transactions](https://www.dwellir.com/docs/aptos/sponsored_transactions) - Gasless key rotation

---

## ledger_info

# ledger_info

## Overview

Retrieve current ledger information and chain metadata. This is the most fundamental Aptos REST API endpoint, returning the chain ID, current epoch, latest ledger version, block height, and node role. It serves as the starting point for transaction building, sync verification, and network health monitoring.

## Endpoint

`GET /v1`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1" \
      -H "Accept: application/json"
```

## Response Fields

- `chain_id` (`integer, required`): Network identifier: `1` = mainnet, `2` = testnet, other values for devnets
- `epoch` (`string, required`): Current epoch number; increments when the validator set changes
- `ledger_version` (`string, required`): Latest committed transaction version (global sequence number)
- `oldest_ledger_version` (`string, required`): Oldest version available on this node (pruned nodes start higher than 0)
- `ledger_timestamp` (`string, required`): Timestamp of the latest committed transaction in microseconds since Unix epoch
- `block_height` (`string, required`): Latest block height (number of blocks since genesis)
- `oldest_block_height` (`string, required`): Oldest block available on this node
- `node_role` (`string, required`): Node type: `full_node` or `validator`
- `git_hash` (`string, required`): Git commit hash of the node software

## Successful Response

```json
{
  "chain_id": 1,
  "epoch": "5000",
  "ledger_version": "500000000",
  "oldest_ledger_version": "0",
  "ledger_timestamp": "1700000000000000",
  "block_height": "150000000",
  "oldest_block_height": "0",
  "node_role": "full_node",
  "git_hash": "abc123..."
}
```

## Error Responses

### Error 1

- Description: Network identifier: `1` = mainnet, `2` = testnet, other values for devnets

### Error 2

- Description: Current epoch number; increments when the validator set changes

### Error 3

- Description: Latest committed transaction version (global sequence number)

### Error 4

- Description: Oldest version available on this node (pruned nodes start higher than 0)

### Error 5

- Description: Timestamp of the latest committed transaction in microseconds since Unix epoch

### Error 6

- Description: Latest block height (number of blocks since genesis)

### Error 7

- Description: Oldest block available on this node

### Error 8

- Description: Node type: `full_node` or `validator`

### Error 9

- Description: Git commit hash of the node software

## Code Examples

cURL
Python
TypeScript
Rust

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
info = client.info()

print(f"Chain ID:       {info['chain_id']}")
print(f"Epoch:          {info['epoch']}")
print(f"Ledger version: {info['ledger_version']}")
print(f"Block height:   {info['block_height']}")

# Convert timestamp to human-readable
import datetime
ts_seconds = int(info['ledger_timestamp']) / 1_000_000
dt = datetime.datetime.fromtimestamp(ts_seconds, tz=datetime.timezone.utc)
print(f"Latest block:   {dt.isoformat()}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

const ledgerInfo = await aptos.getLedgerInfo();
console.log(`Chain ID:    ${ledgerInfo.chain_id}`);
console.log(`Epoch:       ${ledgerInfo.epoch}`);
console.log(`Version:     ${ledgerInfo.ledger_version}`);
console.log(`Block height: ${ledgerInfo.block_height}`);

// Check sync freshness
const ageMs = Date.now() - Number(ledgerInfo.ledger_timestamp) / 1000;
console.log(`Data age: ${(ageMs / 1000).toFixed(1)}s`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let info = client.get_ledger_information().await?;

println!("Chain ID: {}", info.inner().chain_id);
println!("Epoch: {}", info.inner().epoch);
println!("Version: {}", info.inner().version);
println!("Block height: {}", info.inner().block_height);
```

## Use Cases

1. **Transaction Building**: Retrieve the current chain ID and ledger version, both required for constructing valid transactions. The chain ID prevents replay attacks across different networks (mainnet vs testnet).

2. **Sync Status Verification**: Monitor the node's sync progress by comparing `ledger_version` and `ledger_timestamp` against known network state. A `ledger_timestamp` more than 30 seconds behind real time indicates the node is not fully synced.

3. **Historical Query Planning**: Use `oldest_ledger_version` and `oldest_block_height` to determine the range of available historical data. Queries before these values will fail with 404 errors.

4. **Block Explorer Navigation**: Power block explorers with the current height and version, enabling users to navigate to the latest blocks and understand overall chain progress.

5. **Time-Based Queries**: Convert between wall-clock time and ledger versions using `ledger_timestamp`. This enables retrieving state "as of" a specific point in time by finding the corresponding version.

6. **Network Health Monitoring**: Track `epoch` changes to monitor validator set transitions. Track `ledger_timestamp` progression to detect chain stalls or slowdowns. Alert when the gap between real time and ledger time exceeds a threshold.

7. **Multi-Node Comparison**: Query multiple API endpoints and compare `ledger_version` values to verify they are in sync. A large version gap between providers may indicate network partitions or node issues.

## Response Fields Deep Dive

### chain\_id

The chain ID is a critical safety parameter:

- **Mainnet**: `1`
- **Testnet**: `2`
- **Devnet**: varies by deployment

Always validate `chain_id` matches your expected network before submitting transactions. Submitting a mainnet transaction to testnet (or vice versa) will fail due to chain ID mismatch in the signature.

### epoch

Epochs define validator set boundaries. When the epoch increments:

- The validator set may change (new validators join, existing ones leave)
- Staking rewards are distributed
- Governance proposals may take effect

Epoch duration on mainnet is approximately 2 hours. Track epoch transitions to monitor governance and staking events.

### ledger\_version vs block\_height

These are related but distinct concepts:

- `ledger_version` counts individual transactions (global sequence number)
- `block_height` counts blocks (each containing one or more transactions)
- A single block contains `last_version - first_version + 1` transactions

Use `ledger_version` for transaction-level operations and `block_height` for block-level operations.

### ledger\_timestamp

The timestamp is in **microseconds** since Unix epoch (not milliseconds or seconds). Common conversions:

- To seconds: divide by 1,000,000
- To milliseconds: divide by 1,000
- To JavaScript Date: `new Date(Number(timestamp) / 1000)`

## Best Practices

**Caching Strategy**: Ledger info changes every block (approximately every 4 seconds on mainnet). Cache this data for 2-5 seconds to minimize API calls while maintaining reasonable freshness.

**Chain ID Validation**: Always verify `chain_id` matches your expected network before building transactions. This single check prevents accidental cross-network submissions.

**Version Management**: When building transactions, fetch fresh ledger info to get the latest version. Stale version data can cause transaction expiration issues or references to pruned state.

**Historical Data Awareness**: Check `oldest_ledger_version` before attempting historical queries. Nodes prune old data, and querying beyond the oldest version returns errors.

**Timestamp Precision**: The `ledger_timestamp` is in microseconds since Unix epoch. Ensure your timestamp conversion handles this correctly -- a common bug is treating it as milliseconds.

**Health Check Integration**: Incorporate this endpoint into your application's health checks. If `ledger_timestamp` is more than 30 seconds behind, treat the API as degraded and consider failing over to an alternative endpoint.

## Performance Considerations

This endpoint is highly optimized and typically responds in 10-30ms. It queries metadata from the node's fast-access cache layer without touching the transaction database or blockchain state.

Response size is minimal (under 1KB), making it suitable for frequent polling. For applications requiring real-time updates, poll every 4-5 seconds (matching block time) rather than faster, which provides no additional freshness.

The `node_role` field indicates whether you are querying a validator or fullnode. Dwellir's API endpoints are fullnodes, which provide stable, scalable access with minimal lag behind validators (typically 1-2 seconds).

## Related Endpoints

- `/v1/-/healthy` - Quick health check endpoint (returns 200 if node is healthy)
- `/v1/blocks/by_height/{height}` - Fetch a specific block by height
- `/v1/transactions` - List recent transactions starting from the latest version
- `/v1/spec` - OpenAPI specification for all available endpoints

---

## module_structure

# module_structure

Move modules are the fundamental building blocks of smart contracts on Aptos. They encapsulate types, functions, and constants into reusable, composable units that can be published on-chain and invoked by transactions. Understanding proper module structure is essential for building maintainable, secure, and efficient smart contracts.

## Overview

Move modules define types (structs), functions (both public and private), and constants under a specific account address. On Aptos, modules are published to the blockchain under an account address and become immutable once deployed, though they can be upgraded following specific compatibility rules. Each module has a unique identifier consisting of the publishing account address and the module name (e.g., `0x1::coin`).

## Module Anatomy

A well-structured Move module typically contains the following components organized in a logical order:

```move
module 0x1::example_token {
    // 1. Imports
    use std::signer;
    use std::string::String;
    use aptos_framework::event;
    use aptos_framework::timestamp;

    // 2. Error codes
    const ENOT_AUTHORIZED: u64 = 1;
    const EINSUFFICIENT_BALANCE: u64 = 2;
    const EALREADY_INITIALIZED: u64 = 3;

    // 3. Constants
    const MAX_SUPPLY: u64 = 1_000_000_000;
    const DECIMALS: u8 = 8;

    // 4. Structs (types)
    struct TokenStore has key {
        balance: u64,
        frozen: bool
    }

    struct Capabilities has key {
        mint_cap: MintCapability,
        burn_cap: BurnCapability
    }

    struct MintCapability has store, drop {}
    struct BurnCapability has store, drop {}

    // 5. Events
    struct TransferEvent has drop, store {
        from: address,
        to: address,
        amount: u64,
        timestamp: u64
    }

    // 6. Event handles
    struct EventStore has key {
        transfer_events: event::EventHandle<TransferEvent>
    }

    // 7. Public entry functions (transaction entry points)
    public entry fun initialize(account: &signer) {
        // Implementation
    }

    public entry fun transfer(from: &signer, to: address, amount: u64) acquires TokenStore, EventStore {
        // Implementation
    }

    // 8. Public functions (callable by other modules)
    public fun balance_of(addr: address): u64 acquires TokenStore {
        // Implementation
        0
    }

    // 9. Public(friend) functions
    public(friend) fun mint(cap: &MintCapability, to: address, amount: u64) acquires TokenStore {
        // Implementation
    }

    // 10. Internal functions
    fun internal_transfer(from: address, to: address, amount: u64) acquires TokenStore {
        // Implementation
    }

    // 11. Helper functions
    fun validate_amount(amount: u64): bool {
        amount > 0 && amount <= MAX_SUPPLY
    }
}
```

## Abilities and Type Safety

Move's ability system provides fine-grained control over how types can be used. The four abilities are:

- **key**: Can be stored as a top-level resource under an account (required for global storage)
- **store**: Can be stored inside other structs with the `key` ability
- **copy**: Values can be copied (duplicated)
- **drop**: Values can be implicitly discarded

Use abilities judiciously to enforce proper resource semantics and prevent common vulnerabilities like resource duplication or accidental deletion.

## Real-World Use Cases

1. **Token Contracts**: Structure modules to manage fungible or non-fungible token supply, balances, and metadata with proper access controls and event emissions.

2. **DeFi Protocols**: Organize complex financial logic into separate modules for lending, borrowing, liquidity pools, and governance, each with clear interfaces.

3. **NFT Marketplaces**: Create modular systems with separate modules for listings, bids, royalties, and transfers to facilitate maintainability and upgrades.

4. **Governance Systems**: Structure voting, proposal, and execution logic into distinct modules with clear separation of concerns.

5. **Access Control Systems**: Build reusable authentication and authorization modules that other contracts can leverage through public interfaces.

6. **Oracle Integration**: Create structured modules that fetch and validate external data with proper error handling and event emission.

## Best Practices

**Minimize Entry Function Logic**: Keep entry functions thin by delegating complex logic to internal functions. This improves testability and code reuse.

**Emit Comprehensive Events**: Emit events for all state changes to enable off-chain indexing and user notifications. Include relevant context in event data.

**Error Code Organization**: Define all error codes as module constants at the top of the file with descriptive names prefixed with 'E'.

**Function Ordering**: Follow a consistent ordering pattern (entry functions first, then public, then internal) to improve code readability.

**Use Friend Declarations**: Leverage the `friend` visibility modifier to expose functions only to trusted modules while maintaining encapsulation.

**Document Acquires**: Always document which resources a function acquires to help developers understand global storage access patterns.

**Separate Concerns**: Split large modules into smaller, focused modules that each handle a specific concern or feature.

## Module Dependencies

```move
// In Move.toml
[dependencies]
AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", subdir = "aptos-move/framework/aptos-framework", rev = "mainnet" }

[addresses]
my_project = "_"
```

Properly manage dependencies in your Move.toml file and use named addresses to make modules portable across different deployment environments.

## Related Concepts

- [Upgradability](https://www.dwellir.com/docs/aptos/upgradability) - Learn how to safely upgrade modules
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Understand Move's resource model
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Best practices for testing module functionality
- [View Functions](https://www.dwellir.com/docs/aptos/view_functions) - Implement gas-free read operations

---

## multi_agent

# multi_agent

Multi-agent transactions enable multiple independent signers to authorize a single transaction on Aptos, allowing complex operations that require coordination between different accounts. This feature is essential for marketplaces, atomic swaps, escrow services, and any scenario where multiple parties must agree to a state change.

## Overview

Traditional blockchain transactions have a single sender who pays gas and authorizes all operations. Multi-agent transactions extend this model by allowing additional signers (secondary signers) to authorize operations on their own resources within the same atomic transaction. All signers must provide signatures before the transaction can execute, ensuring all parties consent to the operation.

## Technical Implementation

Multi-agent transactions use a special transaction payload that includes addresses of all required signers. Each signer must sign the same transaction hash, and the transaction only executes if all signatures are valid.

```move
module 0x1::marketplace {
    use std::signer;
    use aptos_framework::coin;
    use aptos_framework::aptos_coin::AptosCoin;

    struct Listing has key {
        price: u64,
        owner: address
    }

    struct NFT has key, store {
        id: u64,
        metadata: vector<u8>
    }

    // Requires both buyer and seller signatures
    public entry fun purchase_nft(
        buyer: &signer,
        seller: &signer,
        nft_id: u64
    ) acquires NFT, Listing {
        let seller_addr = signer::address_of(seller);
        let buyer_addr = signer::address_of(buyer);

        // Get listing from seller
        let listing = borrow_global<Listing>(seller_addr);
        let price = listing.price;

        // Transfer payment from buyer to seller
        coin::transfer<AptosCoin>(buyer, seller_addr, price);

        // Transfer NFT from seller to buyer
        let nft = move_from<NFT>(seller_addr);
        move_to(buyer, nft);

        // Clean up listing
        let Listing { price: _, owner: _ } = move_from<Listing>(seller_addr);
    }
}
```

## Creating Multi-Agent Transactions

### TypeScript SDK

```typescript
import { Aptos, Account, AccountAuthenticator } from "@aptos-labs/ts-sdk";

const aptos = new Aptos();

// Create accounts
const buyer = Account.generate();
const seller = Account.generate();

// Build multi-agent transaction
const transaction = await aptos.transaction.build.multiAgent({
  sender: buyer.accountAddress,
  secondarySignerAddresses: [seller.accountAddress],
  data: {
    function: "0x1::marketplace::purchase_nft",
    functionArguments: [seller.accountAddress, 123]
  }
});

// Both parties sign
const buyerAuth = aptos.transaction.sign({ signer: buyer, transaction });
const sellerAuth = aptos.transaction.sign({ signer: seller, transaction });

// Submit with all signatures
const committedTxn = await aptos.transaction.submit.multiAgent({
  transaction,
  senderAuthenticator: buyerAuth,
  additionalSignersAuthenticators: [sellerAuth]
});

await aptos.waitForTransaction({ transactionHash: committedTxn.hash });
```

### Python SDK

```python
from aptos_sdk.client import RestClient
from aptos_sdk.account import Account
from aptos_sdk.transactions import EntryFunction, TransactionPayload

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

buyer = Account.generate()
seller = Account.generate()

# Build multi-agent transaction
payload = EntryFunction.natural(
    "0x1::marketplace",
    "purchase_nft",
    [],
    [seller.address(), 123]
)

# Create and sign with both accounts
signed_txn = client.create_multi_agent_bcs_transaction(
    buyer, [seller], payload
)

tx_hash = client.submit_bcs_transaction(signed_txn)
client.wait_for_transaction(tx_hash)
```

## Real-World Use Cases

1. **NFT Marketplaces**: Execute atomic NFT sales where the buyer transfers payment and seller transfers the NFT in a single transaction, eliminating front-running or partial execution risks.

2. **Atomic Swaps**: Enable trustless peer-to-peer token swaps where both parties exchange assets simultaneously without requiring an intermediary or escrow.

3. **Joint Account Operations**: Implement shared accounts or vaults that require multiple parties to approve withdrawals or significant operations.

4. **Escrow Releases**: Coordinate between buyer, seller, and escrow agent to release funds and assets when conditions are met.

5. **Multi-Party Gaming**: Execute game moves or state transitions that require agreement from multiple players in competitive or cooperative games.

6. **Cross-Protocol Operations**: Coordinate actions across different protocols where multiple protocol administrators must authorize complex operations.

## Advanced Patterns

### Three-Way Transactions

```move
public entry fun escrow_complete(
    buyer: &signer,
    seller: &signer,
    escrow_agent: &signer,
    item_id: u64
) acquires EscrowedItem, Payment {
    // All three parties must sign
    // Agent verifies conditions
    // Buyer gets item
    // Seller gets payment
    // Agent gets fee
}
```

### Conditional Multi-Agent

```move
public entry fun conditional_transfer(
    sender: &signer,
    receiver: &signer,
    amount: u64,
    condition_met: bool
) {
    assert!(condition_met, ECONDITION_NOT_MET);
    // Both parties acknowledge the condition is met
    // Execute transfer
}
```

## Best Practices

**Verify All Signers**: Always validate that all required signers have provided valid signatures before executing critical operations.

**Atomic Operations**: Structure multi-agent transactions to be truly atomic - either all operations succeed or all fail together.

**Clear Responsibilities**: Document which signer pays gas (primary signer) and what each secondary signer authorizes.

**Timeout Mechanisms**: Implement expiration times for multi-agent transaction proposals to prevent indefinite pending states.

**Off-Chain Coordination**: Use off-chain communication channels to coordinate signature collection before submitting the final transaction.

**Gas Estimation**: The primary signer pays all gas fees, so ensure they have sufficient funds for the entire transaction.

**Order Independence**: Design functions so that the order of secondary signers doesn't matter when possible to simplify coordination.

## Signature Collection Flow

1. **Transaction Construction**: Primary signer builds the transaction with all secondary signer addresses
2. **Hash Distribution**: Share the transaction hash with all secondary signers
3. **Signature Collection**: Each party signs the transaction hash independently
4. **Aggregation**: Primary signer collects all signatures
5. **Submission**: Submit the fully-signed transaction to the network
6. **Atomic Execution**: All operations execute or fail together

## Security Considerations

**Signature Verification**: Never submit a multi-agent transaction without verifying all signatures are authentic and match expected signers.

**Transaction Inspection**: All signers should inspect the transaction details before signing to ensure they agree with the operations.

**Replay Protection**: Multi-agent transactions include sequence numbers to prevent replay attacks.

**Partial Signing Attacks**: Protect against scenarios where some signers might try to modify the transaction after others have signed.

**Resource Ownership**: Verify that all signers actually own or control the resources the transaction will modify.

## Comparison with Multi-Sig

Multi-agent transactions differ from multi-sig accounts:

- **Multi-Agent**: Multiple independent accounts coordinate on a single transaction
- **Multi-Sig**: Single account controlled by multiple keys with threshold approval

Multi-agent is better for:

- Peer-to-peer interactions between distinct parties
- Operations involving resources from multiple accounts
- One-time coordinated actions

Multi-sig is better for:

- Shared account management
- Corporate treasury controls
- Ongoing governance of a single account

## Related Concepts

- [Sponsored Transactions](https://www.dwellir.com/docs/aptos/sponsored_transactions) - Separate signer and fee payer
- [Resource Accounts](https://www.dwellir.com/docs/aptos/resource_accounts) - Autonomous contract accounts
- [Key Rotation](https://www.dwellir.com/docs/aptos/key_rotation) - Change account keys
- [Object Model](https://www.dwellir.com/docs/aptos/object_model) - Alternative ownership patterns

---

## object_model

# object_model

The Aptos object model introduces a powerful abstraction for managing complex digital assets with rich composition, flexible ownership, and extensible functionality. Objects provide globally addressable, heterogeneous resources that support ownership hierarchies, reference relationships, and safe composition patterns not easily achievable with traditional Move resources.

## Overview

Objects in Aptos are special on-chain entities identified by unique addresses, combining the benefits of resource safety with flexible ownership and composition capabilities. Unlike traditional resources stored directly under user accounts, objects have their own addresses and can own other objects, creating hierarchical ownership structures ideal for NFTs, composable game items, and complex DeFi positions.

## Core Concepts

### Object Creation

Objects are created with unique addresses and can store multiple heterogeneous resources:

```move
module 0x1::nft_collection {
    use std::string::String;
    use aptos_framework::object::{Self, Object, ConstructorRef};
    use aptos_token_objects::token;
    use aptos_token_objects::collection;

    struct CollectionMetadata has key {
        creator: address,
        description: String,
        max_supply: u64,
        minted: u64
    }

    struct TokenMetadata has key {
        name: String,
        description: String,
        uri: String,
        rarity: u8
    }

    // Create a collection object
    public entry fun create_collection(
        creator: &signer,
        name: String,
        description: String,
        max_supply: u64,
        uri: String
    ) {
        let constructor_ref = collection::create_unlimited_collection(
            creator,
            description,
            name,
            option::none(),
            uri
        );

        let object_signer = object::generate_signer(&constructor_ref);
        move_to(&object_signer, CollectionMetadata {
            creator: signer::address_of(creator),
            description,
            max_supply,
            minted: 0
        });
    }

    // Create an NFT object within the collection
    public entry fun mint_nft(
        creator: &signer,
        collection: String,
        name: String,
        description: String,
        uri: String,
        rarity: u8
    ) {
        let constructor_ref = token::create_named_token(
            creator,
            collection,
            description,
            name,
            option::none(),
            uri
        );

        let object_signer = object::generate_signer(&constructor_ref);
        move_to(&object_signer, TokenMetadata {
            name,
            description,
            uri,
            rarity
        });
    }
}
```

## Key Features

### Object References

Objects can reference other objects safely without ownership transfer:

```move
struct GameCharacter has key {
    name: String,
    level: u64,
    equipped_weapon: Object<Weapon>,  // Reference to weapon object
    inventory: vector<Object<Item>>   // References to item objects
}

public fun equip_weapon(
    character_obj: Object<GameCharacter>,
    weapon_obj: Object<Weapon>
) acquires GameCharacter {
    let character = borrow_global_mut<GameCharacter>(object::object_address(&character_obj));
    character.equipped_weapon = weapon_obj;
}
```

### Ownership Transfer

Objects support flexible ownership patterns:

```move
use aptos_framework::object;

public entry fun transfer_nft(
    owner: &signer,
    nft: Object<TokenMetadata>,
    recipient: address
) {
    // Transfer object ownership
    object::transfer(owner, nft, recipient);
}

public entry fun make_soulbound(creator: &signer, nft: Object<TokenMetadata>) {
    // Disable transfers permanently
    let transfer_ref = object::generate_transfer_ref(creator, nft);
    object::disable_ungated_transfer(&transfer_ref);
}
```

### Object Composition

Objects can own other objects, creating hierarchies:

```move
struct Bundle has key {
    items: vector<Object<Item>>,
    total_value: u64
}

public fun create_bundle(
    creator: &signer,
    item_objects: vector<Object<Item>>
) {
    let constructor_ref = object::create_object(signer::address_of(creator));
    let object_signer = object::generate_signer(&constructor_ref);

    // Transfer items to the bundle object
    let i = 0;
    while (i < vector::length(&item_objects)) {
        let item = *vector::borrow(&item_objects, i);
        object::transfer(creator, item, signer::address_of(&object_signer));
        i = i + 1;
    };

    move_to(&object_signer, Bundle {
        items: item_objects,
        total_value: 0 // Calculate from items
    });
}
```

## Real-World Use Cases

1. **Composable NFTs**: Create NFTs that can own other NFTs, like a character owning equipment, a house containing furniture, or a card deck containing individual cards.

2. **DeFi Positions**: Represent complex financial positions as objects that aggregate multiple assets, track performance, and enable atomic position transfers.

3. **Gaming Assets**: Build rich game systems where items, characters, and locations are objects with properties, inventories, and relationships.

4. **Fractional Ownership**: Create objects representing shared ownership of assets where multiple parties hold stakes in a single valuable item.

5. **Licensing and Royalties**: Implement objects that track usage rights, royalty obligations, and derivative relationships between creative works.

6. **Supply Chain Tracking**: Model physical goods as objects that move through a supply chain, accumulating provenance and certification data.

## Best Practices

**Use Objects for Complex Assets**: Prefer objects over simple resources when assets need ownership transfer, composition, or references to other assets.

**Leverage Object Addresses**: Objects have stable addresses independent of owner, enabling reliable references and off-chain indexing.

**Implement Access Control**: Use object extensions and permissions to control who can modify object properties or transfer ownership.

**Consider Gas Costs**: Object operations involve additional overhead compared to simple resources; balance flexibility with efficiency.

**Plan Ownership Hierarchies**: Design clear ownership structures to prevent circular references and simplify asset management.

**Utilize Events**: Emit events for object creation, transfer, and modification to enable off-chain tracking and indexing.

**Test Composition Patterns**: Thoroughly test complex object hierarchies to ensure proper cleanup and prevent orphaned resources.

## Object Capabilities

Objects support various capabilities through refs:

```move
// Transfer control
let transfer_ref = object::generate_transfer_ref(&constructor_ref);
object::disable_ungated_transfer(&transfer_ref);

// Deletion control
let delete_ref = object::generate_delete_ref(&constructor_ref);
object::delete(delete_ref);

// Extension control
let extend_ref = object::generate_extend_ref(&constructor_ref);
let object_signer = object::generate_signer_for_extending(&extend_ref);
```

## Querying Objects

```move
#[view]
public fun get_owner(obj: Object<TokenMetadata>): address {
    object::owner(obj)
}

#[view]
public fun is_owner(obj: Object<TokenMetadata>, potential_owner: address): bool {
    object::is_owner(obj, potential_owner)
}

#[view]
public fun can_transfer(obj: Object<TokenMetadata>): bool {
    object::ungated_transfer_allowed(obj)
}
```

## Object vs Traditional Resources

**Use Objects When:**

- Assets need to be transferred between users
- Complex composition or hierarchies are required
- Stable addresses independent of owner are beneficial
- References between assets are needed

**Use Traditional Resources When:**

- Simple account-scoped data storage
- No transfer or ownership changes needed
- Minimizing gas costs is critical
- Simple key-value storage suffices

## Related Concepts

- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Traditional resource patterns
- [Multi-Agent Transactions](https://www.dwellir.com/docs/aptos/multi_agent) - Transfer objects atomically
- [View Functions](https://www.dwellir.com/docs/aptos/view_functions) - Query object properties
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Test object composition

---

## orderless_transactions

# orderless_transactions

Orderless transactions on Aptos reduce head-of-line blocking by allowing flexible transaction ordering using explicit nonces instead of strict sequence numbers. This feature enables parallel transaction submission and improves throughput for accounts issuing multiple independent operations simultaneously.

## Overview

Traditional blockchain accounts process transactions in strict sequential order based on sequence numbers, where transaction N+1 cannot execute until transaction N completes. Orderless transactions relax this constraint by using nonces that allow transactions to execute in any order when dependencies permit, significantly improving performance for high-frequency accounts like exchanges, bots, and payment processors.

## How It Works

Instead of requiring sequential processing, orderless transactions use nonces to indicate independence:

```move
// Traditional: Must execute in order 0, 1, 2, 3...
Transaction { sequence_number: 0 }
Transaction { sequence_number: 1 }  // Blocks on 0
Transaction { sequence_number: 2 }  // Blocks on 1

// Orderless: Can execute in any order
Transaction { nonce: 1 }  // Independent
Transaction { nonce: 2 }  // Independent
Transaction { nonce: 3 }  // Independent
```

## Implementation

```typescript
import { Aptos, Account } from "@aptos-labs/ts-sdk";

const aptos = new Aptos();
const account = Account.generate();

// Submit multiple transactions with different nonces
const nonce1 = 100;
const nonce2 = 101;
const nonce3 = 102;

// All can execute in parallel
const tx1 = await aptos.transaction.build.simple({
  sender: account.accountAddress,
  data: {
    function: "0x1::coin::transfer",
    functionArguments: [recipient1, 1000]
  },
  options: { nonce: nonce1 }
});

const tx2 = await aptos.transaction.build.simple({
  sender: account.accountAddress,
  data: {
    function: "0x1::coin::transfer",
    functionArguments: [recipient2, 2000]
  },
  options: { nonce: nonce2 }
});

// Submit both immediately
await aptos.transaction.submit.simple({ transaction: tx1, senderAuthenticator: auth1 });
await aptos.transaction.submit.simple({ transaction: tx2, senderAuthenticator: auth2 });
```

## Real-World Use Cases

1. **Exchange Operations**: Crypto exchanges can submit thousands of withdrawal transactions simultaneously without waiting for sequential processing, dramatically increasing throughput.

2. **Payment Processors**: Payment platforms can process multiple customer payments in parallel rather than queuing them sequentially.

3. **Bot Operations**: Trading bots and automated market makers can submit multiple independent transactions without head-of-line blocking from slower operations.

4. **Batch Processing**: Applications can submit large batches of independent transactions that execute as capacity permits rather than in strict order.

5. **Multi-User Services**: Services managing operations for multiple users can interleave transactions without artificial ordering constraints.

6. **Retry Logic**: Failed transactions can be retried with new nonces while other transactions continue processing without blocking.

## Best Practices

**Use Unique Nonces**: Ensure each transaction from an account uses a unique nonce to prevent conflicts and rejections.

**Track Nonce Usage**: Maintain a nonce counter or registry to avoid accidentally reusing nonces across concurrent operations.

**Handle Race Conditions**: Implement proper error handling for nonce conflicts that may occur in distributed systems.

**Monitor Nonce Gaps**: Track which nonces have been used to identify failed or pending transactions.

**Set Reasonable Limits**: Don't create excessive nonce gaps as validators may have limits on nonce range acceptance.

**Combine with Sequence Numbers**: Use orderless transactions for truly independent operations while using sequence numbers for dependent chains.

## Limitations

- Nonces must be managed carefully to avoid conflicts
- Not all transaction types support orderless execution
- May require changes to existing transaction submission infrastructure
- Validators may impose limits on maximum nonce values or gaps
- Ordering guarantees between transactions are reduced

## Comparison with Sequential Transactions

**Sequential (Traditional)**

- Guaranteed execution order
- Simpler to reason about dependencies
- Natural retry semantics
- Lower throughput under load

**Orderless (Nonce-based)**

- Higher throughput potential
- Parallel execution capability
- More complex nonce management
- Better for independent operations

## Monitoring and Debugging

```typescript
// Track nonce usage
const usedNonces = new Set<number>();

function getNextNonce(): number {
  let nonce = Math.floor(Math.random() * 1000000);
  while (usedNonces.has(nonce)) {
    nonce = Math.floor(Math.random() * 1000000);
  }
  usedNonces.add(nonce);
  return nonce;
}

// Check transaction status by nonce
async function checkNonceStatus(account: address, nonce: number) {
  // Query blockchain for transaction with specific nonce
}
```

## Related Concepts

- [Aggregator V2](https://www.dwellir.com/docs/aptos/aggregator_v2) - Parallel state updates
- [Sponsored Transactions](https://www.dwellir.com/docs/aptos/sponsored_transactions) - Flexible fee payment
- [Multi-Agent Transactions](https://www.dwellir.com/docs/aptos/multi_agent) - Multi-party coordination

---

## real_time

> Coming soon: Need support for this? Email <support@dwellir.com> if you want early access

# real_time

Real-time transaction streaming provides millisecond-latency access to finalized Aptos transactions as they occur on-chain, enabling responsive applications, trading systems, monitoring tools, and live dashboards. This feature delivers transactions with minimal delay after consensus, significantly faster than polling-based approaches.

## Overview

Real-time streaming subscribes to the head of the blockchain, delivering transactions immediately as they are finalized by consensus. This approach provides consistent low-latency updates without the overhead and inconsistency of polling, ideal for applications requiring immediate awareness of on-chain state changes.

## Basic Real-Time Subscription

```typescript
import { TransactionStreamClient } from "./generated/aptos_stream";
import { credentials, Metadata } from "@grpc/grpc-js";

const client = new TransactionStreamClient(
  "stream.aptos.dwellir.com:443",
  credentials.createSsl()
);

const metadata = new Metadata();
metadata.add("authorization", `Bearer ${process.env.API_KEY}`);

// Subscribe to head of chain (no starting version = latest)
const request = {
  includeEvents: true,
  includeChanges: true
};

const stream = client.subscribe(request, metadata);

stream.on("data", (transaction) => {
  console.log(`New transaction: ${transaction.hash}`);
  console.log(`Version: ${transaction.version}`);
  console.log(`Timestamp: ${transaction.timestamp}`);

  // Process transaction in real-time
  processTransaction(transaction);
});

stream.on("error", (error) => {
  console.error("Stream error:", error);
  // Implement reconnection logic
});

stream.on("end", () => {
  console.log("Stream ended");
  // Reconnect
});
```

## Real-World Use Cases

1. **Trading Bots**: Execute arbitrage or market-making strategies based on millisecond-fresh DEX swap data and price movements.

2. **Live Dashboards**: Display real-time protocol metrics, transaction volumes, gas prices, and network activity without refresh delays.

3. **Notification Systems**: Send instant push notifications when users receive payments, NFTs transfer, or important events occur.

4. **Gaming Applications**: Update game state, leaderboards, and player inventories immediately as transactions finalize on-chain.

5. **Security Monitoring**: Detect suspicious patterns, unusual transactions, or potential attacks in real-time for immediate response.

6. **Price Oracles**: Publish up-to-the-second price feeds by processing DEX trades and liquidity changes as they happen.

## Best Practices

**Handle Reconnections Gracefully**: Implement automatic reconnection with exponential backoff when streams disconnect to maintain continuous coverage.

**Track Last Processed Version**: Persist the last successfully processed version to resume streams without gaps after restarts.

**Implement Buffering**: Buffer incoming transactions during processing spikes to prevent backpressure and dropped connections.

**Monitor Stream Health**: Track message rates, latency, and error rates to detect and respond to degradation quickly.

**Use Asynchronous Processing**: Process transactions asynchronously to avoid blocking the stream and falling behind.

**Set Appropriate Timeouts**: Configure keepalive and timeout values to detect dead connections quickly.

**Handle Duplicates**: Implement idempotent processing since network issues may cause duplicate transaction deliveries.

## Event Filtering

```typescript
class RealTimeProcessor {
  private stream: ClientReadableStream;

  async start() {
    const request = {
      includeEvents: true,
      // Optional: filter by transaction type
      transactionFilters: {
        userTransaction: true,
        genesisTransaction: false,
        blockMetadataTransaction: false,
        stateCheckpointTransaction: false
      }
    };

    this.stream = client.subscribe(request, metadata);

    this.stream.on("data", (tx) => {
      // Further filter by event type
      if (this.isRelevantTransaction(tx)) {
        this.processTransaction(tx);
      }
    });
  }

  private isRelevantTransaction(tx: Transaction): boolean {
    // Filter by function calls
    if (tx.payload?.function) {
      return tx.payload.function.startsWith("0x1::coin::transfer");
    }

    // Filter by events
    if (tx.events) {
      return tx.events.some(event =>
        event.type.includes("WithdrawEvent") ||
        event.type.includes("DepositEvent")
      );
    }

    return false;
  }
}
```

## Latency Optimization

```typescript
class LowLatencyProcessor {
  private processingQueue: Transaction[] = [];
  private processing: boolean = false;

  onTransaction(tx: Transaction) {
    this.processingQueue.push(tx);

    if (!this.processing) {
      this.processBatch();
    }
  }

  private async processBatch() {
    this.processing = true;

    while (this.processingQueue.length > 0) {
      const batch = this.processingQueue.splice(0, 100);

      // Process batch in parallel
      await Promise.all(
        batch.map(tx => this.processTransactionFast(tx))
      );
    }

    this.processing = false;
  }

  private async processTransactionFast(tx: Transaction) {
    // Minimal processing for lowest latency
    const essential = this.extractEssentialData(tx);
    await this.fastWrite(essential);
    this.emitEvent(essential);
  }
}
```

## Connection Management

```typescript
class StreamManager {
  private stream: ClientReadableStream | null = null;
  private reconnectAttempts: number = 0;
  private maxReconnectAttempts: number = 10;

  async connect() {
    try {
      this.stream = client.subscribe(request, metadata);

      this.stream.on("data", (tx) => {
        this.reconnectAttempts = 0; // Reset on successful data
        this.handleTransaction(tx);
      });

      this.stream.on("error", (error) => {
        console.error("Stream error:", error);
        this.reconnect();
      });

      this.stream.on("end", () => {
        console.log("Stream ended");
        this.reconnect();
      });

    } catch (error) {
      console.error("Connection error:", error);
      this.reconnect();
    }
  }

  private async reconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error("Max reconnection attempts reached");
      return;
    }

    this.reconnectAttempts++;
    const backoff = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);

    console.log(`Reconnecting in ${backoff}ms (attempt ${this.reconnectAttempts})`);

    await this.sleep(backoff);
    await this.connect();
  }

  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  disconnect() {
    if (this.stream) {
      this.stream.cancel();
      this.stream = null;
    }
  }
}
```

## Metrics and Monitoring

```typescript
class StreamMetrics {
  private lastTransactionTime: number = Date.now();
  private transactionCount: number = 0;
  private latencies: number[] = [];

  recordTransaction(tx: Transaction, receivedAt: number) {
    this.transactionCount++;
    this.lastTransactionTime = receivedAt;

    // Calculate latency (time from tx timestamp to receipt)
    const txTime = new Date(tx.timestamp).getTime();
    const latency = receivedAt - txTime;
    this.latencies.push(latency);

    // Keep only recent latencies
    if (this.latencies.length > 1000) {
      this.latencies.shift();
    }
  }

  getMetrics() {
    const avgLatency = this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length;
    const timeSinceLastTx = Date.now() - this.lastTransactionTime;

    return {
      totalProcessed: this.transactionCount,
      averageLatency: avgLatency.toFixed(0) + "ms",
      timeSinceLastTransaction: timeSinceLastTx + "ms",
      isHealthy: timeSinceLastTx < 5000 // Alert if > 5s without tx
    };
  }
}
```

## Performance Considerations

- **Network Proximity**: Deploy processors close to streaming endpoints to minimize network latency
- **Concurrent Processing**: Use worker pools to process transactions in parallel without blocking the stream
- **Memory Management**: Implement backpressure handling to prevent memory exhaustion during bursts
- **Database Optimization**: Use connection pooling and bulk operations for database writes
- **Caching**: Cache frequently accessed data to avoid database lookups during processing

## Related Concepts

- [Streaming Overview](https://www.dwellir.com/docs/aptos/streaming/overview) - Introduction to streaming
- [Historical Replay](https://www.dwellir.com/docs/aptos/historical_replay) - Processing past transactions
- [Custom Processors](https://www.dwellir.com/docs/aptos/custom_processors) - Building stream processors
- [Authentication](https://www.dwellir.com/docs/aptos/authentication) - Securing stream connections

---

## resource_accounts

# resource_accounts

Resource accounts are special autonomous accounts on Aptos that have no private key and can only be controlled by smart contracts. They enable developers to create stable, deterministic addresses for protocols while maintaining programmatic control over account operations, making them essential for DeFi protocols, DAOs, and autonomous systems.

## Overview

Resource accounts solve the problem of protocol-controlled accounts that need stable addresses but shouldn't have private keys that could be lost or compromised. They are created with deterministic addresses derived from a source account and seed, allowing protocols to own assets, publish modules, and execute operations entirely through smart contract logic.

## Creating Resource Accounts

```move
module 0x1::protocol {
    use std::signer;
    use aptos_framework::resource_account;
    use aptos_framework::account;

    struct ResourceAccountCap has key {
        cap: account::SignerCapability
    }

    // Create a resource account during protocol initialization
    public entry fun initialize(deployer: &signer, seed: vector<u8>) {
        // Create resource account with deterministic address
        let (resource_signer, signer_cap) = account::create_resource_account(
            deployer,
            seed
        );

        // Store capability to use resource account later
        move_to(deployer, ResourceAccountCap { cap: signer_cap });

        // Resource account can now hold assets, publish modules
        // Address is deterministic: derived from deployer + seed
    }

    // Use resource account for protocol operations
    public entry fun protocol_transfer(
        amount: u64,
        recipient: address
    ) acquires ResourceAccountCap {
        let cap = borrow_global<ResourceAccountCap>(@deployer);
        let resource_signer = account::create_signer_with_capability(&cap.cap);

        // Resource account executes transfer
        coin::transfer<AptosCoin>(&resource_signer, recipient, amount);
    }
}
```

## Deterministic Addresses

Resource account addresses are deterministically computed:

```move
// Address formula: hash(source_address, seed, 0xFF)
let resource_addr = account::create_resource_address(&source_address, seed);
```

This enables:

- Predictable protocol addresses before deployment
- Consistent addresses across different networks
- Easy verification of protocol authenticity

## Real-World Use Cases

1. **DeFi Protocols**: Create liquidity pools, vaults, and treasury accounts that are controlled by protocol logic rather than private keys, eliminating single points of failure.

2. **DAO Treasuries**: Establish autonomous treasuries where funds can only be moved through governance proposals and smart contract execution.

3. **Escrow Services**: Build trustless escrow systems where locked assets are held by resource accounts with programmatic release conditions.

4. **Protocol Upgrades**: Deploy protocol modules to resource accounts, enabling controlled upgrade paths through governance rather than admin keys.

5. **Cross-Chain Bridges**: Operate bridge accounts that custody locked assets with release controlled by multi-sig validation logic.

6. **Automated Market Makers**: Run AMM contracts where liquidity provider funds are held in resource accounts managed by protocol mathematics.

## Best Practices

**Secure Signer Capabilities**: Store `SignerCapability` in a protected resource with appropriate access controls. Never expose it directly.

**Use Meaningful Seeds**: Choose descriptive seeds that indicate the resource account's purpose (e.g., b"liquidity\_pool\_v2").

**Implement Access Control**: Add authorization logic to functions that use resource account capabilities.

**Test Address Generation**: Verify resource account addresses are computed correctly before mainnet deployment.

**Document Ownership**: Clearly document which modules control which resource accounts for auditing and verification.

**Capability Rotation**: Consider patterns for safely transferring or revoking resource account control if needed.

**Avoid Seed Collisions**: Use unique seeds to prevent accidentally creating multiple resource accounts at the same address.

## Advanced Patterns

### Multi-Tier Resource Accounts

```move
// Create hierarchy of resource accounts
public fun create_protocol_structure(creator: &signer) {
    let (treasury_signer, treasury_cap) = account::create_resource_account(
        creator,
        b"treasury"
    );

    let (rewards_signer, rewards_cap) = account::create_resource_account(
        creator,
        b"rewards"
    );

    let (insurance_signer, insurance_cap) = account::create_resource_account(
        creator,
        b"insurance"
    );

    // Store capabilities for different protocol functions
}
```

### Controlled Capability Distribution

```move
struct GovernanceCap has key {
    treasury_cap: account::SignerCapability,
    withdraw_limit: u64,
    last_withdraw: u64
}

public fun governed_withdraw(
    amount: u64,
    recipient: address
) acquires GovernanceCap {
    let gov = borrow_global_mut<GovernanceCap>(@governance);

    // Enforce time-locks and limits
    assert!(amount <= gov.withdraw_limit, EEXCEEDS_LIMIT);

    let resource_signer = account::create_signer_with_capability(&gov.treasury_cap);
    coin::transfer(&resource_signer, recipient, amount);

    gov.last_withdraw = timestamp::now_seconds();
}
```

## Security Considerations

**Capability Storage**: The `SignerCapability` is extremely powerful - treat it like a master private key and protect it appropriately.

**Access Control**: Implement robust access control for any function that uses resource account capabilities.

**Upgrade Safety**: If resource accounts publish modules, carefully manage upgrade policies to prevent malicious changes.

**Resource Exhaustion**: Resource accounts need gas for operations, so ensure they maintain sufficient APT balances.

**Deterministic Generation**: Understand that anyone can compute your resource account address from the source and seed.

## Querying Resource Accounts

```move
#[view]
public fun get_resource_account_address(source: address, seed: vector<u8>): address {
    account::create_resource_address(&source, seed)
}

#[view]
public fun get_protocol_treasury(): address {
    account::create_resource_address(&@deployer, b"treasury")
}
```

## Comparison with Regular Accounts

**Resource Accounts:**

- No private key
- Controlled by smart contracts
- Deterministic addresses
- Perfect for protocols

**Regular Accounts:**

- Have private keys
- User-controlled
- Random addresses
- For individual users

## Related Concepts

- [Multi-Agent Transactions](https://www.dwellir.com/docs/aptos/multi_agent) - Coordinate with resource accounts
- [Sponsored Transactions](https://www.dwellir.com/docs/aptos/sponsored_transactions) - Pay gas for resource accounts
- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Deploy modules to resource accounts
- [Key Rotation](https://www.dwellir.com/docs/aptos/key_rotation) - Not applicable to resource accounts

---

## resource_management

# resource_management

Resource management is one of Move's most distinctive and powerful features, providing built-in safety guarantees that prevent common blockchain vulnerabilities. Move's resource model ensures that digital assets cannot be copied, accidentally lost, or double-spent through compiler-enforced linear type semantics and explicit resource handling patterns.

## Overview

Resources in Move are special types marked with the `key` ability that represent digital assets or critical state. Unlike regular data structures, resources have strict lifecycle rules: they cannot be copied or implicitly destroyed, must be explicitly moved between storage locations, and can only exist in one place at a time. This linear type system eliminates entire classes of vulnerabilities that plague other blockchain platforms.

## Resource Lifecycle

Resources follow a strict lifecycle from creation to destruction:

```move
module 0x1::resource_example {
    use std::signer;

    // Resource definition - key ability required for global storage
    struct Vault has key {
        balance: u64,
        owner: address
    }

    // Creating a resource
    public entry fun create_vault(account: &signer, initial_balance: u64) {
        let vault = Vault {
            balance: initial_balance,
            owner: signer::address_of(account)
        };
        // Move resource into global storage
        move_to(account, vault);
    }

    // Accessing a resource - requires acquires annotation
    public fun get_balance(addr: address): u64 acquires Vault {
        let vault_ref = borrow_global<Vault>(addr);
        vault_ref.balance
    }

    // Modifying a resource
    public entry fun deposit(account: &signer, amount: u64) acquires Vault {
        let addr = signer::address_of(account);
        let vault_ref = borrow_global_mut<Vault>(addr);
        vault_ref.balance = vault_ref.balance + amount;
    }

    // Moving resource out of storage
    public entry fun destroy_vault(account: &signer) acquires Vault {
        let addr = signer::address_of(account);
        let Vault { balance: _, owner: _ } = move_from<Vault>(addr);
        // Resource is destructured and destroyed
    }
}
```

## Acquires Annotation

The `acquires` keyword is mandatory for functions that access global resources. It serves both as documentation and as a compiler check to prevent reentrancy vulnerabilities:

```move
// Function that reads from one resource and writes to another
public fun transfer_between_vaults(
    from: address,
    to: address,
    amount: u64
) acquires Vault {
    let from_vault = borrow_global_mut<Vault>(from);
    from_vault.balance = from_vault.balance - amount;

    let to_vault = borrow_global_mut<Vault>(to);
    to_vault.balance = to_vault.balance + amount;
}

// Multiple resource types require listing all
public fun complex_operation(addr: address) acquires Vault, UserProfile, Settings {
    // Can access all three resource types
}
```

## Resource Patterns

### Capability Pattern

Use capability resources to control access to privileged operations:

```move
struct MintCapability has key, store {
    total_minted: u64
}

public fun mint_with_cap(
    cap: &mut MintCapability,
    recipient: address,
    amount: u64
) {
    cap.total_minted = cap.total_minted + amount;
    // Mint logic here
}
```

### Witness Pattern

Use one-time witnesses for initialization guarantees:

```move
struct COIN has drop {}

public fun initialize(witness: COIN, account: &signer) {
    // Can only be called once because witness is consumed
}
```

### Hot Potato Pattern

Create types without drop ability to force handling:

```move
struct Receipt {
    amount: u64,
    must_be_consumed: bool
}

public fun create_receipt(): Receipt {
    Receipt { amount: 100, must_be_consumed: true }
}

public fun consume_receipt(receipt: Receipt) {
    let Receipt { amount: _, must_be_consumed: _ } = receipt;
}
```

## Real-World Use Cases

1. **Token Implementations**: Manage token balances as resources to guarantee conservation of supply and prevent unauthorized minting or burning through compiler-enforced linearity.

2. **NFT Ownership**: Represent unique digital assets as resources that cannot be duplicated, ensuring true scarcity and provable ownership on-chain.

3. **Vault Systems**: Build secure storage mechanisms where assets can only be accessed by authorized parties, leveraging resource semantics for safety.

4. **Permission Systems**: Create capability resources that grant specific permissions, ensuring that access rights cannot be forged or duplicated.

5. **Escrow Services**: Hold resources in escrow with guaranteed atomic transfers, where assets must either complete the full transfer or remain in the original location.

6. **Gaming Assets**: Represent in-game items as resources with unique properties that cannot be duplicated or destroyed outside intended game mechanics.

## Best Practices

**Always Document Acquires**: Explicitly list all resource types a function accesses in the acquires clause, even when it seems obvious. This helps prevent subtle bugs.

**Check Resource Existence**: Use `exists<T>(address)` before borrowing resources to handle cases where resources might not be initialized.

**Avoid Cross-Module Resource Access**: Design modules to manage their own resources rather than accessing resources defined in other modules when possible.

**Use Immutable Borrows**: Prefer `borrow_global` over `borrow_global_mut` when only reading data to reduce potential for concurrent access issues.

**Structured Destruction**: When moving resources out of storage, always destructure them completely to ensure all fields are properly handled.

**Resource Initialization**: Provide clear initialization functions and document preconditions for resource creation to prevent misuse.

**Capability Distribution**: Carefully control how and when capability resources are created and distributed to maintain system security.

## Common Patterns

```move
// Check-then-access pattern
if (exists<Vault>(addr)) {
    let vault = borrow_global<Vault>(addr);
    // Use vault
};

// Modify with validation
public entry fun safe_withdraw(account: &signer, amount: u64) acquires Vault {
    let addr = signer::address_of(account);
    assert!(exists<Vault>(addr), EVAULT_NOT_FOUND);

    let vault = borrow_global_mut<Vault>(addr);
    assert!(vault.balance >= amount, EINSUFFICIENT_BALANCE);
    vault.balance = vault.balance - amount;
}
```

## Related Concepts

- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Organize resource definitions effectively
- [Formal Verification](https://www.dwellir.com/docs/aptos/formal_verification) - Prove resource invariants mathematically
- [Object Model](https://www.dwellir.com/docs/aptos/object_model) - Alternative resource composition approach
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Test resource lifecycle operations

---

## spec

# spec

## Overview

Open the Aptos REST API explorer page served by the node. On Dwellir Aptos mainnet, `/v1/spec` returns an interactive Stoplight HTML document rather than a raw JSON or YAML OpenAPI payload.

## Endpoint

`GET /v1/spec`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# Open the interactive explorer page
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec" \
      -H "Accept: application/json"

    # Save the HTML explorer page locally
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec" \
      -H "Accept: application/json" \
      -o aptos-spec.html
```

## Response Fields

- `result` (`OBJECT, required`): ### Success Response (200) Returns an HTML document (`content-type: text/html; charset=utf-8`) that embeds the Aptos Node API explorer: ```html <!doctype html> <html lang="en"> <head> <title>Aptos Node API</title> <script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script> </head> <body> <elements-api apiDescriptionUrl="/openapi.yaml"></elements-api> </body> </html> ``` Treat this endpoint as a human-facing interactive explorer page, not a machine-readable artifact for direct SDK generation.

## Successful Response

```html
<!doctype html>
<html lang="en">
  <head>
    <title>Aptos Node API</title>
    <script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
  </head>
  <body>
    <elements-api apiDescriptionUrl="/openapi.yaml"></elements-api>
  </body>
</html>
```

## Code Examples

cURL
Python
TypeScript

```bash
# Open the interactive explorer page
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec" \
  -H "Accept: application/json"

# Save the HTML explorer page locally
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec" \
  -H "Accept: application/json" \
  -o aptos-spec.html
```

```python
import requests
response = requests.get(
    "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec",
    headers={"Accept": "application/json"}
)
print(response.headers["content-type"])
html = response.text

# Save the Stoplight explorer page
with open("aptos-spec.html", "w") as f:
    f.write(html)
```

```typescript
// Fetch the explorer page
const response = await fetch(
  "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/spec",
  { headers: { "Accept": "application/json" } }
);
const html = await response.text();
console.log(response.headers.get("content-type"));
console.log(html.includes("Aptos Node API"));
```

## Integration with Development Tools

### Browser Explorer

Use the returned page as a human-facing explorer in a browser tab. If you need machine-readable schemas for code generation, source them from a verified raw spec artifact rather than `/v1/spec`.

## Use Cases

1. **Interactive API Exploration**: Open the embedded Stoplight page to browse endpoints, request examples, and schemas manually.
2. **Onboarding and Support**: Share one URL with developers who need to inspect the REST surface without installing extra tooling.
3. **Quick Endpoint Discovery**: Verify paths, parameters, and response descriptions while debugging integrations.

## Best Practices

**Version Pinning**: The specification matches the API version of the node you are querying. For production applications, pin to a specific specification version and test thoroughly before upgrading.

**Caching**: The specification changes only when the node software is upgraded. Cache it locally during development rather than fetching on every build. Re-fetch when you detect node version changes via the `git_hash` field in ledger info.

**Format Selection**: On Dwellir Aptos mainnet, `/v1/spec` returns HTML even when you request JSON. Do not treat this endpoint as a raw OpenAPI download target for parsers or code generators.

## Performance Considerations

The explorer document is moderately large and rarely changes. Cache it locally if you need an offline copy for documentation review.

This endpoint does not touch blockchain state. Response times are primarily driven by serving the static HTML explorer page and its embedded assets.

Avoid fetching the explorer at application runtime unless you are explicitly linking users to documentation.

## Related Resources

- Aptos TypeScript SDK (`@aptos-labs/ts-sdk`) uses this specification as a basis for type definitions
- OpenAPI Generator documentation at openapi-generator.tech
- OpenAPI 3.0 specification documentation at swagger.io
- `/v1` - Get ledger info including node software version

---

## sponsored_transactions

# sponsored_transactions

Sponsored transactions enable gas fee delegation on Aptos, allowing one account (the sponsor) to pay transaction fees for another account (the user). This feature is crucial for onboarding new users, providing gasless experiences, and building accessible dApps that don't require users to hold native tokens before interacting with applications.

## Overview

Traditional blockchain interactions require users to hold native tokens (APT on Aptos) to pay gas fees, creating a significant onboarding barrier. Sponsored transactions separate the transaction sender from the fee payer, enabling applications, DAOs, or services to subsidize user transactions while maintaining proper authentication and authorization.

## How Sponsored Transactions Work

A sponsored transaction involves three parties:

1. **User (Sender)**: Signs the transaction to authorize their operations
2. **Sponsor (Fee Payer)**: Pays gas fees and signs to authorize payment
3. **Network**: Validates both signatures and executes the transaction

```typescript
import { Aptos, Account } from "@aptos-labs/ts-sdk";

const aptos = new Aptos();

// User who wants to perform an operation but has no APT
const user = Account.generate();

// Sponsor who will pay the gas fees
const sponsor = Account.generate(); // Must have APT

// Build sponsored transaction
const transaction = await aptos.transaction.build.simple({
  sender: user.accountAddress,
  data: {
    function: "0x1::aptos_account::transfer",
    functionArguments: [recipientAddress, 1000]
  },
  withFeePayer: true
});

// User signs their part
const userAuth = aptos.transaction.sign({
  signer: user,
  transaction
});

// Sponsor signs to pay fees
const sponsorAuth = aptos.transaction.signAsFeePayer({
  signer: sponsor,
  transaction
});

// Submit with both signatures
const committedTxn = await aptos.transaction.submit.simple({
  transaction,
  senderAuthenticator: userAuth,
  feePayerAuthenticator: sponsorAuth
});

await aptos.waitForTransaction({ transactionHash: committedTxn.hash });
```

## Move Implementation

```move
module 0x1::sponsored_service {
    use std::signer;
    use aptos_framework::coin;
    use aptos_framework::aptos_coin::AptosCoin;

    struct SponsorshipConfig has key {
        enabled: bool,
        daily_limit: u64,
        per_user_limit: u64
    }

    struct UserQuota has key {
        used_today: u64,
        last_reset: u64
    }

    // User function that can be sponsored
    public entry fun perform_action(user: &signer, data: vector<u8>) {
        // User's actual operation
        // Gas will be paid by sponsor, not user
    }

    // Sponsor checks if they should sponsor this user
    public entry fun check_sponsorship_eligibility(
        user: address,
        estimated_gas: u64
    ): bool acquires UserQuota, SponsorshipConfig {
        let config = borrow_global<SponsorshipConfig>(@sponsor);
        if (!config.enabled) return false;

        if (!exists<UserQuota>(user)) return true;

        let quota = borrow_global<UserQuota>(user);
        quota.used_today + estimated_gas <= config.per_user_limit
    }
}
```

## Real-World Use Cases

1. **User Onboarding**: Let new users interact with your dApp immediately without requiring them to acquire APT first, dramatically reducing onboarding friction.

2. **Gaming Applications**: Sponsor in-game transactions so players can play without worrying about gas fees, creating a seamless gaming experience.

3. **Social Media dApps**: Enable users to post, like, and interact on blockchain social platforms without paying gas for each action.

4. **Enterprise Applications**: Corporations can sponsor transactions for their employees or customers, internalizing blockchain interaction costs.

5. **Loyalty Programs**: Reward loyal users by sponsoring their transactions as a benefit, similar to traditional fee waivers.

6. **Micropayments**: Enable micro-transaction use cases where gas fees would otherwise be prohibitively expensive relative to transaction value.

## Best Practices

**Implement Rate Limiting**: Protect sponsors from abuse by implementing per-user quotas, daily limits, and velocity checks.

**Verify User Intent**: Ensure the sponsored transaction represents genuine user intent and hasn't been manipulated.

**Gas Estimation**: Accurately estimate gas costs before sponsoring to prevent unexpected costs and set appropriate budgets.

**Conditional Sponsorship**: Only sponsor transactions that meet specific criteria (new users, specific functions, within limits).

**Monitor Costs**: Track total sponsorship costs and set alerts for unusual patterns or excessive spending.

**Graceful Degradation**: Have fallback options if sponsorship limits are reached so users can still complete transactions.

**Transparent Terms**: Clearly communicate sponsorship terms, limits, and conditions to users.

## Advanced Sponsorship Patterns

### Conditional Sponsorship

```typescript
async function conditionalSponsor(
  user: Account,
  transaction: any,
  sponsor: Account
): Promise<boolean> {
  // Check if user qualifies for sponsorship
  const userTxCount = await getUserTransactionCount(user.accountAddress);
  const isNewUser = userTxCount < 10;

  if (isNewUser) {
    // Sponsor new users
    return true;
  }

  // Check if user has loyalty points
  const loyaltyPoints = await getLoyaltyPoints(user.accountAddress);
  if (loyaltyPoints > 100) {
    // Sponsor loyal users
    return true;
  }

  return false; // User must pay own fees
}
```

### Tiered Sponsorship

```move
struct TieredSponsorship has key {
    bronze_limit: u64,  // Sponsor up to X gas
    silver_limit: u64,  // Sponsor up to Y gas
    gold_limit: u64,    // Sponsor up to Z gas
}

public fun get_user_tier(user: address): u8 {
    // Determine user tier based on activity, holdings, etc.
    1 // Bronze
}
```

### Pooled Sponsorship

```move
struct SponsorshipPool has key {
    contributors: vector<address>,
    total_funds: u64,
    used_funds: u64,
}

public fun contribute_to_pool(contributor: &signer, amount: u64) {
    // Add funds to sponsorship pool
    // Multiple parties share sponsorship costs
}
```

## Cost Management

```typescript
// Track sponsorship costs
interface SponsorshipMetrics {
  totalSponsored: number;
  transactionsSponsored: number;
  averageCostPerTx: number;
  dailyBudget: number;
  remainingBudget: number;
}

async function checkBudget(sponsor: Account): Promise<boolean> {
  const metrics = await getSponsorshipMetrics(sponsor.accountAddress);
  return metrics.remainingBudget > metrics.averageCostPerTx;
}
```

## Security Considerations

**Sybil Resistance**: Implement mechanisms to prevent users from creating multiple accounts to exploit sponsorship limits.

**Validation Before Signing**: Sponsors must validate transaction contents before signing to prevent sponsoring malicious operations.

**Budget Controls**: Set strict budget limits and alerts to prevent sponsorship pool depletion.

**Abuse Prevention**: Monitor for unusual patterns that might indicate coordinated abuse of sponsorship systems.

**Emergency Shutdown**: Implement ability to quickly disable sponsorship if abuse is detected.

## Monitoring and Analytics

```typescript
// Track sponsorship metrics
async function trackSponsorship(txHash: string, sponsor: address, user: address, gasPaid: number) {
  await database.insert({
    timestamp: Date.now(),
    txHash,
    sponsor,
    user,
    gasPaid,
    type: 'sponsored_transaction'
  });

  await updateDailyMetrics(sponsor);
  await checkAbusePatterns(user);
}
```

## Related Concepts

- [Multi-Agent Transactions](https://www.dwellir.com/docs/aptos/multi_agent) - Multiple signers in transactions
- [Resource Accounts](https://www.dwellir.com/docs/aptos/resource_accounts) - Programmatic transaction execution
- [Key Rotation](https://www.dwellir.com/docs/aptos/key_rotation) - Sponsor key management
- [Orderless Transactions](https://www.dwellir.com/docs/aptos/orderless_transactions) - High-throughput sponsorship

---

## Transaction Streaming Overview

> Coming soon: Need support for this? Email <support@dwellir.com> if you want early access

# Transaction Streaming Overview

The Aptos streaming service delivers ordered, finalized transactions over gRPC with support for both historical replay and real-time tails. It is aimed at indexers, analytics backends, bots, and monitoring systems that need a continuous feed instead of repeated REST polling.

## What This Gives You

- **real-time tails** for new finalized transactions
- **replay from a known starting point** when you need to backfill or recover
- **one long-lived connection** instead of many short polling requests
- **ordered delivery** so downstream processors can checkpoint progress cleanly

Compared with REST and GraphQL, streaming is the better fit when your system needs low-latency ingestion or durable catch-up after restarts.

## Connection Model

The stream is authenticated with bearer-token metadata and then kept open as a long-lived gRPC subscription. The stronger examples elsewhere in this section use a TLS endpoint and standard gRPC metadata:

```typescript
import { credentials, Metadata } from "@grpc/grpc-js";

const metadata = new Metadata();
metadata.add("authorization", `Bearer ${process.env.DWELLIR_API_KEY}`);

// The concrete endpoint is provisioned during streaming onboarding.
// See the linked authentication and real-time guides for the full client setup.
const client = new TransactionStreamClient(
  "stream.aptos.dwellir.com:443",
  credentials.createSsl()
);
```

## Operating Patterns

### Real-Time Consumers

Start from the head of chain when you want live notifications, live dashboards, or alerting systems that only care about new transactions.

### Replay and Recovery

Start from a previously stored version when a worker restarts or when you need to backfill a gap. This is the safer model for production because it lets you prove that no committed transactions were skipped.

### Hybrid Pipelines

Many production systems replay to a checkpoint first and then transition into a live tail on the same service. That keeps the ingestion model consistent whether the worker is catching up or operating at the head of chain.

## Implementation Checklist

- Persist the last fully processed version so reconnects can resume without gaps.
- Make downstream writes idempotent. Network retries can cause the same transaction to be observed again.
- Handle backpressure explicitly; do not let slow consumers block the stream reader.
- Monitor reconnect frequency, stream lag, and message throughput so degraded consumers are visible before they fall behind.
- Keep REST or GraphQL available for targeted lookups, retries, and operator debugging.

## Related Guides

- [Streaming Authentication](https://www.dwellir.com/docs/aptos/authentication) for bearer-token setup
- [Real-Time Streaming](https://www.dwellir.com/docs/aptos/real_time) for tailing the head of chain
- [Historical Replay](https://www.dwellir.com/docs/aptos/historical_replay) for backfills and recovery
- [Custom Processors](https://www.dwellir.com/docs/aptos/custom_processors) for worker architecture patterns

---

## subscriptions

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# subscriptions

GraphQL subscriptions enable real-time data streaming from the Aptos blockchain, allowing applications to receive instant notifications when on-chain events occur. This feature is essential for responsive user interfaces, live dashboards, trading bots, and any application requiring immediate updates without polling.

## Overview

Unlike traditional queries that fetch data once, subscriptions establish persistent connections that stream updates as blockchain state changes. When a new block is added, transactions are processed, or specific events occur, subscribed clients receive notifications immediately, enabling truly reactive blockchain applications.

## Core Subscription Patterns

### New Transactions

```graphql
subscription NewTransactions($address: String!) {
  user_transactions(
    where: {
      _or: [
        { sender: { _eq: $address } },
        { receiver: { _eq: $address } }
      ]
    },
    order_by: { version: desc },
    limit: 1
  ) {
    hash
    sender
    version
    success
    gas_used
    timestamp
  }
}
```

### Balance Changes

```graphql
subscription BalanceUpdates($owner: String!, $asset_type: String!) {
  current_fungible_asset_balances(
    where: {
      owner_address: { _eq: $owner },
      asset_type: { _eq: $asset_type }
    }
  ) {
    amount
    last_transaction_version
    last_transaction_timestamp
  }
}
```

### NFT Transfers

```graphql
subscription NftActivity($collection: String!) {
  token_activities_v2(
    where: {
      token_data: {
        collection_id: { _eq: $collection }
      }
    },
    order_by: { transaction_version: desc },
    limit: 1
  ) {
    transaction_version
    from_address
    to_address
    token_data_id
    type
    transaction_timestamp
  }
}
```

### New Blocks

```graphql
subscription NewBlocks {
  ledger_infos(
    order_by: { version: desc },
    limit: 1
  ) {
    chain_id
    version
    block_height
    epoch
    block_timestamp
  }
}
```

### Event Monitoring

```graphql
subscription EventStream($address: String!, $event_type: String!) {
  events(
    where: {
      account_address: { _eq: $address },
      type: { _eq: $event_type }
    },
    order_by: { transaction_version: desc },
    limit: 1
  ) {
    sequence_number
    type
    data
    transaction_version
    transaction_timestamp
  }
}
```

## Real-World Use Cases

1. **Live Wallets**: Update balances, transaction histories, and token holdings in real-time as blockchain state changes without manual refreshing.

2. **Trading Interfaces**: Stream price updates, order fills, and liquidity changes instantly for responsive trading experiences on DEXes.

3. **Notification Systems**: Alert users immediately when they receive payments, NFT transfers, or other important on-chain events.

4. **Live Dashboards**: Display real-time protocol metrics, transaction volumes, active users, and other statistics with instant updates.

5. **Gaming Applications**: Stream game state changes, item transfers, and player actions in real-time for interactive blockchain games.

6. **Monitoring Tools**: Track smart contract interactions, detect anomalies, and monitor system health with instant event notifications.

## Best Practices

**Handle Reconnections**: Implement automatic reconnection logic with exponential backoff when subscription connections drop.

**Manage Connection Limits**: Be aware of concurrent subscription limits and reuse connections where possible.

**Filter Aggressively**: Use precise WHERE clauses to receive only relevant updates and reduce bandwidth usage.

**Implement Debouncing**: For rapid updates, debounce UI updates to prevent overwhelming the interface with changes.

**Fallback to Polling**: Have polling-based fallbacks for environments where WebSocket connections aren't available.

**Validate Events**: Always validate subscription payloads before processing to handle schema changes gracefully.

**Monitor Performance**: Track subscription latency and message rates to ensure responsive user experiences.

## TypeScript Implementation

```typescript
import { ApolloClient, gql, InMemoryCache } from "@apollo/client";
import { WebSocketLink } from "@apollo/client/link/ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
import ws from "ws";

// Create WebSocket client
const wsClient = new SubscriptionClient(
  "wss://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql",
  {
    reconnect: true,
    connectionParams: {
      headers: {
        "X-API-Key": "YOUR_API_KEY"
      }
    }
  },
  ws
);

const link = new WebSocketLink(wsClient);

const client = new ApolloClient({
  link,
  cache: new InMemoryCache()
});

// Subscribe to transactions
function subscribeToTransactions(address: string, callback: (tx: any) => void) {
  const subscription = client.subscribe({
    query: gql`
      subscription NewTransactions($address: String!) {
        user_transactions(
          where: {
            _or: [
              { sender: { _eq: $address } },
              { receiver: { _eq: $address } }
            ]
          },
          order_by: { version: desc },
          limit: 1
        ) {
          hash
          sender
          success
          timestamp
        }
      }
    `,
    variables: { address }
  });

  return subscription.subscribe({
    next: (result) => callback(result.data.user_transactions[0]),
    error: (error) => console.error("Subscription error:", error)
  });
}

// Usage
const unsubscribe = subscribeToTransactions("0x123...", (transaction) => {
  console.log("New transaction:", transaction);
  // Update UI with new transaction
});

// Clean up
// unsubscribe();
```

## Advanced Patterns

### Combined Updates

```typescript
// Subscribe to multiple data streams
function subscribeToWallet(address: string) {
  // Transactions
  const txSub = client.subscribe({
    query: TRANSACTION_SUBSCRIPTION,
    variables: { address }
  });

  // Balance changes
  const balanceSub = client.subscribe({
    query: BALANCE_SUBSCRIPTION,
    variables: { address }
  });

  // NFT transfers
  const nftSub = client.subscribe({
    query: NFT_SUBSCRIPTION,
    variables: { address }
  });

  return {
    unsubscribe: () => {
      txSub.unsubscribe();
      balanceSub.unsubscribe();
      nftSub.unsubscribe();
    }
  };
}
```

### Conditional Subscriptions

```typescript
// Only subscribe when needed
let subscription: any = null;

function startMonitoring(address: string) {
  if (subscription) return;

  subscription = subscribeToTransactions(address, (tx) => {
    if (tx.success) {
      notifyUser(`Transaction ${tx.hash} confirmed`);
    }
  });
}

function stopMonitoring() {
  if (subscription) {
    subscription.unsubscribe();
    subscription = null;
  }
}
```

### Debounced Updates

```typescript
import { debounce } from "lodash";

const updateUI = debounce((data: any) => {
  // Update UI with latest data
  render(data);
}, 100);

subscribeToBalances(address, (balance) => {
  updateUI(balance);
});
```

## WebSocket Connection Management

```typescript
// Robust connection handling
class SubscriptionManager {
  private client: SubscriptionClient;
  private subscriptions: Map<string, any> = new Map();

  constructor(url: string) {
    this.client = new SubscriptionClient(url, {
      reconnect: true,
      reconnectionAttempts: 5,
      connectionParams: {
        headers: { "X-API-Key": process.env.API_KEY }
      }
    });

    this.client.onReconnected(() => {
      console.log("Reconnected - resubscribing...");
      this.resubscribeAll();
    });
  }

  subscribe(id: string, query: any, variables: any, callback: Function) {
    const sub = this.client.request({ query, variables }).subscribe({
      next: (data) => callback(data),
      error: (error) => console.error(`Subscription ${id} error:`, error)
    });

    this.subscriptions.set(id, { sub, query, variables, callback });
    return () => this.unsubscribe(id);
  }

  unsubscribe(id: string) {
    const subscription = this.subscriptions.get(id);
    if (subscription) {
      subscription.sub.unsubscribe();
      this.subscriptions.delete(id);
    }
  }

  private resubscribeAll() {
    this.subscriptions.forEach((sub, id) => {
      this.unsubscribe(id);
      this.subscribe(id, sub.query, sub.variables, sub.callback);
    });
  }
}
```

## Related Concepts

- [GraphQL Overview](https://www.dwellir.com/docs/aptos/graphql/overview) - GraphQL API introduction
- [Streaming API](https://www.dwellir.com/docs/aptos/streaming/overview) - Alternative real-time approach
- [Aggregations](https://www.dwellir.com/docs/aptos/aggregations) - Statistical queries
- [Token Activities](https://www.dwellir.com/docs/aptos/token_activities) - Transaction and transfer monitoring

---

## tables_item

# tables_item

## Overview

Read a single value from a Move state table by its key. Tables in Aptos are key-value stores used by smart contracts to store mappings, balances, configurations, and other structured data. This endpoint provides direct access to individual table entries without fetching the entire resource that contains the table.

## Endpoint

`POST /v1/tables/{table_handle}/item`

## Request Parameters

- `table_handle` (`string, required`): Path parameter: Table handle address (obtained from a resource containing a `Table<K, V>` field)
- `ledger_version` (`string, optional`): Query parameter: Read the table item at a historical ledger version
- `key_type` (`string, required`): Request Body: Move type of the table key (e.g., `address`, `u64`, `0x1::string::String`, or a struct type)
- `value_type` (`string, required`): Request Body: Move type of the table value (e.g., `u128`, `bool`, or a struct type)
- `key` (`any, required`): Request Body: The key to look up, encoded as JSON matching the key_type

## Request Example

```json
{
  "key_type": "address",
  "value_type": "u128",
  "key": "0x619dc29a0aac8fa146714058e8dd6d2d0f3bdf5f6331907bf91f3acd81e6935"
}
```

## Response Fields

- `result` (`OBJECT, required`): ### Success Response (200) Returns the value from the table, decoded according to the specified `value_type`: For simple types (u128, u64, bool, address): ```json "1234567890" ``` For struct types: ```json { "amount": "1000000", "owner": "0x1", "metadata": { "name": "Token", "symbol": "TKN" } } ``` ### Error Responses | Status | Error Code | Description | | -- | -- | -- | | 400 | invalid_input | Invalid table handle, key type, value type, or key format | | 404 | table_item_not_found | Key does not exist in the table | | 404 | table_not_found | Table handle does not exist on-chain |

## Successful Response

```json
"1234567890"
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid table handle, key type, value type, or key format

### Error 2

- Code: `table_item_not_found`
- Description: Key does not exist in the table

### Error 3

- Code: `table_not_found`
- Description: Table handle does not exist on-chain

## Code Examples

> **Tip:** The handle `0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca` references the on-chain coin conversion map. The key shown here is the BCS-encoded metadata address for Aptos Coin, and the returned `u128` value is the circulating supply in octas.

cURL
Python
TypeScript
Rust

```bash
# Query a simple key-value pair
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/tables/0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca/item" \
  -H "Content-Type: application/json" \
  -d '{"key_type":"address","value_type":"u128","key":"0x619dc29a0aac8fa146714058e8dd6d2d0f3bdf5f6331907bf91f3acd81e6935"}'

# Query at a historical version
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/tables/0x1b854.../item?ledger_version=50000000" \
  -H "Content-Type: application/json" \
  -d '{"key_type":"address","value_type":"u128","key":"0x619dc..."}'
```

```python
import requests

BASE_URL = "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"

# Query a table item
response = requests.post(
    f"{BASE_URL}/tables/0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca/item",
    json={
        "key_type": "address",
        "value_type": "u128",
        "key": "0x619dc29a0aac8fa146714058e8dd6d2d0f3bdf5f6331907bf91f3acd81e6935"
    }
)
value = response.json()
print(f"Value: {value}")

# Query with a struct key
response = requests.post(
    f"{BASE_URL}/tables/{handle}/item",
    json={
        "key_type": "0x1::string::String",
        "value_type": "0x1::token::TokenData",
        "key": "my_token_name"
    }
)
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Query a table item
const value = await aptos.getTableItem<string>({
  handle: "0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca",
  data: {
    key_type: "address",
    value_type: "u128",
    key: "0x619dc29a0aac8fa146714058e8dd6d2d0f3bdf5f6331907bf91f3acd81e6935"
  }
});
console.log(`Value: ${value}`);
```

```rust
use aptos_sdk::rest_client::Client;
use serde_json::json;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let value = client.get_table_item(
    "0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca",
    "address",
    "u128",
    "0x619dc29a0aac8fa146714058e8dd6d2d0f3bdf5f6331907bf91f3acd81e6935",
).await?;
println!("Value: {:?}", value.inner());
```

## Discovering Table Handles

Table handles are not easily discoverable -- they are stored as fields within resources. Here is the typical discovery workflow:

1. **Query the resource** containing the table:
   ```bash
   GET /v1/accounts/{address}/resource/{resource_type}
   ```

2. **Find the table field** in the response. Table fields have a `handle` property:
   ```json
   {
     "type": "0x1::coin::CoinInfo<0x1::aptos_coin::AptosCoin>",
     "data": {
       "supply": {
         "vec": [{
           "aggregator": {
             "vec": [{
               "handle": "0x1b854694ae746cdbd8d44186ca4929b2b337df21d1c74633be19b2710552fdca",
               "key": "0x619dc29a..."
             }]
           }
         }]
       }
     }
   }
   ```

3. **Use the handle** to query individual table items.

## Use Cases

1. **Token Supply Tracking**: Query coin supply tables to retrieve current circulation, maximum supply, and minting statistics for any fungible asset.

2. **Configuration Lookups**: Access protocol configuration tables for fee parameters, interest rates, oracle addresses, and governance settings.

3. **Mapping Queries**: Retrieve values from key-value mappings stored in Move tables -- user balances, token ownership records, or application-specific data.

4. **State Verification**: Verify specific state values in smart contracts by directly querying table items rather than fetching and parsing entire resources.

5. **Indexer Alternatives**: For simple key-value lookups, table queries can be more efficient than setting up a GraphQL indexer or processing full resource data.

6. **Analytics and Dashboards**: Build analytics dashboards by querying statistical tables maintained by on-chain protocols.

## Best Practices

**Type Accuracy**: Ensure `key_type` and `value_type` exactly match the table's Move type definitions. Even minor differences (such as `u64` vs `u128`) cause deserialization errors.

**Key Encoding**: Keys must be properly formatted for their type. For addresses, use the full 0x-prefixed hex string. For struct keys, provide a JSON object matching the struct's field layout.

**Handle Discovery**: Table handles are opaque addresses that change when the table is recreated. Always discover handles dynamically from the containing resource rather than hardcoding them.

**404 Handling**: A 404 can mean either the table does not exist or the key does not exist in the table. Check the specific error code (`table_not_found` vs `table_item_not_found`) to distinguish these cases.

**Ledger Version**: Use the optional `ledger_version` query parameter to query tables at historical points in time. This enables time-series analysis, state reconstruction, and consistent cross-table reads.

**Caching**: Table values can change with every transaction that modifies them. Cache based on your knowledge of how frequently the specific key is updated -- protocol parameters change rarely, while user balances change with every transfer.

## Performance Considerations

Table item queries use indexed Merkle tree lookups, typically completing in 50-150ms. Performance depends on key complexity and value size. Simple numeric keys and values are fastest.

Tables are implemented using a Jellyfish Merkle tree structure, providing O(log n) lookup time. Even tables with millions of entries respond quickly due to the logarithmic access pattern.

For bulk queries across many keys in the same table, making concurrent requests is more efficient than sequential queries. However, if you need many entries from the same table, consider whether fetching the entire resource and parsing client-side might be more efficient.

## Related Endpoints

- `/v1/tables/{table_handle}/raw_item` - Read raw BCS-encoded table values
- `/v1/accounts/{address}/resource/{type}` - Fetch the resource containing the table handle
- `/v1/accounts/{address}/resources` - List all resources to discover table handles
- `/v1/view` - Alternative for computed lookups that access tables internally

---

## tables_raw_item

# tables_raw_item

## Endpoint

`POST /v1/tables/{table_handle}/raw_item`

## Request Body

```
{
  "key": { "addr": "0x1" },
  "key_type": "vector<u8>",
  "value_type": "vector<u8>"
}
```

## Response

### Success Response (200)

Returns the raw BCS-encoded bytes as a hex string:

```json
{
  "bytes": "0x0123456789abcdef..."
}
```

The bytes must be decoded using BCS deserialization based on the specified `value_type`.

### Error Responses

| Status | Error Code              | Description                         |
| ------ | ----------------------- | ----------------------------------- |
| 400    | invalid\_input          | Invalid table handle, types, or key |
| 404    | table\_item\_not\_found | Key doesn't exist                   |
| 404    | table\_not\_found       | Table doesn't exist                 |

## Code Examples

```bash
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/tables/0xabc/raw_item" \
  -H "Content-Type: application/json" \
  -d '{"key_type":"vector<u8>","value_type":"vector<u8>","key":"0x00"}'
```

Python example with BCS decoding:

```python
import requests
from aptos_sdk import bcs

response = requests.post(
    "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/tables/{handle}/raw_item",
    json={"key_type": "address", "value_type": "u64", "key": "0x1"}
)
raw_bytes = bytes.fromhex(response.json()['bytes'][2:])  # Remove 0x prefix
value = bcs.deserialize(raw_bytes, bcs.uint64)
```

## Use Cases

Raw table queries serve specialized use cases requiring direct BCS access:

1. **Custom Deserialization**: Deserialize complex or custom Move types that aren't supported by the standard JSON API, using language-specific BCS libraries.

2. **Performance Optimization**: For high-throughput applications, BCS deserialization can be faster than JSON parsing, especially for numeric-heavy data.

3. **Type Flexibility**: Query tables without knowing the exact Move type structure, then decode based on discovered schemas or partial type information.

4. **Cross-Language Compatibility**: BCS is the canonical serialization format for Move. Use raw bytes for interoperability between different SDK implementations.

5. **Verification and Hashing**: Compute cryptographic hashes or signatures over the exact on-chain bytes without JSON encoding ambiguities.

6. **Low-Level Debugging**: Inspect raw serialization format to debug encoding issues or verify data integrity.

## Best Practices

**BCS Knowledge Required**: This endpoint requires deep understanding of BCS serialization and Move type layouts. Use the standard `/tables/{handle}/item` endpoint unless you have specific needs for raw bytes.

**Type Specification**: While you must specify `value_type`, the returned bytes are not validated against this type. It's your responsibility to ensure correct deserialization.

**SDK Support**: Use BCS libraries from official SDKs (aptos-sdk for Python, @aptos-labs/ts-sdk for TypeScript) rather than implementing BCS deserialization yourself.

**Hex Encoding**: Response bytes are 0x-prefixed hex strings. Strip the prefix before converting to byte arrays in most languages.

**Performance Trade-offs**: While BCS deserialization can be faster, the overhead of HTTP requests typically dominates. Only use raw queries if you're batch-processing many items.

## Performance Considerations

Performance characteristics are identical to the standard table item endpoint. The difference is purely in response format - JSON vs raw bytes. Response times are typically 50-150ms.

For applications processing millions of table items, the reduced parsing overhead of BCS can save 10-30% CPU time compared to JSON deserialization, but network I/O remains the bottleneck.

---

## testing

# testing

Testing is crucial for building reliable smart contracts on Aptos. Move provides a comprehensive testing framework that supports unit tests, integration tests, and end-to-end testing scenarios. Proper testing helps catch bugs early, validates business logic, and provides confidence when deploying contracts that manage valuable assets.

## Overview

Move's testing framework allows you to write tests directly in your Move source files using the `#[test]` attribute. Tests can interact with the blockchain state, simulate different signers, and validate both success and failure scenarios. The Aptos CLI provides commands to run tests with detailed output and coverage reporting.

## Unit Testing Basics

Unit tests verify individual functions and modules in isolation:

```move
module 0x1::calculator {
    public fun add(a: u64, b: u64): u64 {
        a + b
    }

    public fun divide(a: u64, b: u64): u64 {
        assert!(b != 0, 1); // EDIVIDE_BY_ZERO
        a / b
    }

    #[test]
    fun test_add() {
        assert!(add(2, 3) == 5, 0);
        assert!(add(0, 0) == 0, 0);
        assert!(add(100, 200) == 300, 0);
    }

    #[test]
    fun test_divide() {
        assert!(divide(10, 2) == 5, 0);
        assert!(divide(100, 10) == 10, 0);
    }

    #[test]
    #[expected_failure(abort_code = 1)]
    fun test_divide_by_zero() {
        divide(10, 0); // Should abort with error code 1
    }
}
```

## Testing with Signers

Test functions can receive signer parameters for testing account-specific logic:

```move
module 0x1::vault {
    use std::signer;

    struct Vault has key {
        balance: u64
    }

    public fun create_vault(account: &signer, initial: u64) {
        move_to(account, Vault { balance: initial });
    }

    public fun deposit(account: &signer, amount: u64) acquires Vault {
        let vault = borrow_global_mut<Vault>(signer::address_of(account));
        vault.balance = vault.balance + amount;
    }

    #[test(account = @0x1)]
    fun test_create_vault(account: &signer) {
        create_vault(account, 100);
        assert!(exists<Vault>(@0x1), 0);
    }

    #[test(account = @0x1)]
    fun test_deposit(account: &signer) acquires Vault {
        create_vault(account, 100);
        deposit(account, 50);
        let vault = borrow_global<Vault>(@0x1);
        assert!(vault.balance == 150, 0);
    }

    #[test(account = @0x1)]
    #[expected_failure]
    fun test_double_create_fails(account: &signer) {
        create_vault(account, 100);
        create_vault(account, 200); // Should fail - resource already exists
    }
}
```

## Multi-Account Testing

Test scenarios involving multiple accounts:

```move
#[test(from = @0x1, to = @0x2)]
fun test_transfer(from: &signer, to: &signer) acquires TokenStore {
    // Setup
    initialize(from, 1000);
    initialize(to, 0);

    // Execute transfer
    transfer(from, signer::address_of(to), 100);

    // Verify balances
    assert!(balance_of(@0x1) == 900, 0);
    assert!(balance_of(@0x2) == 100, 0);
}

#[test(alice = @0x1, bob = @0x2, charlie = @0x3)]
fun test_multi_party_transaction(
    alice: &signer,
    bob: &signer,
    charlie: &signer
) acquires TokenStore {
    // Complex multi-account test scenario
}
```

## Testing Events

Validate that events are emitted correctly:

```move
#[test(account = @0x1)]
fun test_transfer_event(account: &signer) acquires TokenStore, EventStore {
    use aptos_framework::event;

    initialize(account, 1000);

    // Get initial event counter
    let event_store = borrow_global<EventStore>(@0x1);
    let initial_count = event::counter(&event_store.transfer_events);

    // Execute operation
    transfer(account, @0x2, 100);

    // Verify event was emitted
    let final_count = event::counter(&event_store.transfer_events);
    assert!(final_count == initial_count + 1, 0);
}
```

## Test Helpers and Setup

Create helper functions for common test scenarios:

```move
#[test_only]
module 0x1::test_helpers {
    use std::signer;
    use 0x1::vault;

    public fun setup_vault(account: &signer, balance: u64) {
        vault::create_vault(account, balance);
    }

    public fun create_test_accounts(): (signer, signer, signer) {
        // Test-only function to create multiple accounts
    }
}

// Use in tests
#[test(account = @0x1)]
fun test_with_helper(account: &signer) {
    test_helpers::setup_vault(account, 100);
    // Continue test
}
```

## Real-World Use Cases

1. **Token Contract Validation**: Test minting, burning, transfers, and balance tracking to ensure token economics work correctly under all conditions.

2. **Access Control Testing**: Verify that privileged functions can only be called by authorized accounts and that unauthorized access attempts fail properly.

3. **Edge Case Coverage**: Test boundary conditions like zero amounts, maximum values, empty collections, and invalid inputs.

4. **Failure Scenario Testing**: Use `#[expected_failure]` to verify that functions abort correctly when given invalid inputs or when preconditions aren't met.

5. **Upgrade Compatibility**: Test that upgraded modules maintain backward compatibility with existing on-chain state and don't break existing functionality.

6. **Integration Testing**: Test interactions between multiple modules to verify that complex workflows operate correctly end-to-end.

## Best Practices

**Test Both Success and Failure**: Write tests for both happy paths and error conditions. Use `#[expected_failure]` to verify error handling.

**Use Descriptive Test Names**: Name tests clearly to indicate what they're testing (e.g., `test_transfer_insufficient_balance_fails`).

**Keep Tests Independent**: Each test should set up its own state and not depend on execution order or state from other tests.

**Test Edge Cases**: Include tests for boundary values, empty inputs, maximum values, and unusual but valid scenarios.

**Use Test-Only Code**: Mark helper functions and modules with `#[test_only]` to prevent them from being included in production builds.

**Deterministic Testing**: Avoid randomness or time-dependent logic in tests to ensure reproducibility.

**Coverage Goals**: Aim for comprehensive coverage of all public functions and critical internal logic paths.

## Running Tests

```bash
# Run all tests in package
aptos move test --package-dir .

# Run tests with coverage
aptos move test --package-dir . --coverage

# Run specific test
aptos move test --package-dir . --filter test_transfer

# Run tests with gas profiling
aptos move test --package-dir . --gas

# Verbose output
aptos move test --package-dir . --verbose
```

## Advanced Testing Patterns

```move
// Property-based testing pattern
#[test]
fun test_add_commutative() {
    let values = vector[1, 5, 10, 50, 100, 1000];
    let i = 0;
    while (i < vector::length(&values)) {
        let j = 0;
        while (j < vector::length(&values)) {
            let a = *vector::borrow(&values, i);
            let b = *vector::borrow(&values, j);
            assert!(add(a, b) == add(b, a), 0);
            j = j + 1;
        };
        i = i + 1;
    }
}

// State machine testing
#[test(account = @0x1)]
fun test_state_transitions(account: &signer) acquires StateMachine {
    initialize(account);
    assert!(get_state(@0x1) == STATE_INITIAL, 0);

    transition_to_active(account);
    assert!(get_state(@0x1) == STATE_ACTIVE, 0);

    transition_to_complete(account);
    assert!(get_state(@0x1) == STATE_COMPLETE, 0);
}
```

## Related Concepts

- [Formal Verification](https://www.dwellir.com/docs/aptos/formal_verification) - Mathematical proofs of correctness
- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Organize code for testability
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Test resource lifecycle
- [View Functions](https://www.dwellir.com/docs/aptos/view_functions) - Test read-only operations

---

## token_activities

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# token_activities

Token activities tracking provides comprehensive visibility into NFT and token operations on Aptos, including mints, transfers, burns, and marketplace interactions. The GraphQL API enables querying of activity histories, ownership changes, and collection analytics essential for NFT marketplaces, portfolio trackers, and blockchain explorers.

## Overview

Every NFT interaction on Aptos generates events that are indexed and made queryable through GraphQL. Token activities encompass all operations affecting digital assets - from initial minting through transfers, sales, and eventual burning. This data enables applications to display transaction histories, track provenance, analyze trading patterns, and provide real-time notifications for NFT-related events.

## Core Query Patterns

### User Activity History

```graphql
query TokenTransfers($owner: String!, $limit: Int!) {
  token_activities_v2(
    where: { owner_address: { _eq: $owner } },
    limit: $limit,
    order_by: { transaction_version: desc }
  ) {
    transaction_version
    event_account_address
    token_standard
    from_address
    to_address
    token_data_id
    amount
    type
    transaction_timestamp
    token_data {
      token_name
      collection_id
      current_collection {
        collection_name
        creator_address
      }
    }
  }
}
```

### Collection Activity Feed

```graphql
query CollectionActivities($collection_id: String!, $limit: Int!) {
  token_activities_v2(
    where: {
      token_data: {
        collection_id: { _eq: $collection_id }
      }
    },
    order_by: { transaction_version: desc },
    limit: $limit
  ) {
    type
    from_address
    to_address
    token_data_id
    transaction_timestamp
    transaction_version
  }
}
```

### Recent Mints

```graphql
query RecentMints($collection_id: String!) {
  token_activities_v2(
    where: {
      type: { _eq: "0x4::token::MintEvent" },
      token_data: {
        collection_id: { _eq: $collection_id }
      }
    },
    order_by: { transaction_version: desc },
    limit: 50
  ) {
    to_address
    token_data_id
    transaction_timestamp
    transaction_version
  }
}
```

### Sales and Transfers

```graphql
query TokenSales($token_data_id: String!) {
  token_activities_v2(
    where: {
      token_data_id: { _eq: $token_data_id },
      type: { _in: ["0x4::token::TransferEvent", "marketplace_sale"] }
    },
    order_by: { transaction_version: desc }
  ) {
    type
    from_address
    to_address
    transaction_version
    transaction_timestamp
    event_account_address
  }
}
```

### Burn Events

```graphql
query BurnedTokens($collection_id: String!) {
  token_activities_v2(
    where: {
      type: { _eq: "0x4::token::BurnEvent" },
      token_data: {
        collection_id: { _eq: $collection_id }
      }
    },
    order_by: { transaction_version: desc }
  ) {
    token_data_id
    from_address
    transaction_timestamp
  }
}
```

## Real-World Use Cases

1. **NFT Marketplaces**: Display comprehensive activity feeds showing mints, listings, sales, and transfers for collections and individual NFTs with real-time updates.

2. **Portfolio Trackers**: Show users their complete NFT transaction history including acquisitions, sales, transfers, and current holdings across all collections.

3. **Collection Analytics**: Analyze trading volumes, mint rates, holder behavior, and price trends for NFT collections to provide market intelligence.

4. **Provenance Tracking**: Build complete ownership histories showing every transfer from mint to current owner for authenticity verification.

5. **Notification Systems**: Alert users instantly when their NFTs are transferred, listed, sold, or when new items mint in collections they follow.

6. **Marketplace Aggregators**: Combine activity data from multiple marketplaces to provide comprehensive views of NFT trading across the ecosystem.

## Best Practices

**Filter by Activity Type**: Use the type field to distinguish between mints, transfers, burns, and marketplace events for appropriate handling.

**Paginate Results**: Implement cursor-based pagination for activity feeds to efficiently handle collections with high volumes of transactions.

**Join with Token Data**: Always join activities with token\_data to fetch metadata (names, images, attributes) for display.

**Cache Collection Metadata**: Collection-level data changes rarely - cache it aggressively to reduce duplicate queries.

**Handle Missing Data**: Not all activities have complete metadata - implement fallbacks for missing names, images, or attributes.

**Time-Based Filtering**: Use transaction\_timestamp for date range queries when analyzing historical trends or generating reports.

**Aggregate for Statistics**: Use aggregate queries to compute volumes, counts, and other metrics rather than client-side processing.

## TypeScript Integration

```typescript
import { ApolloClient, gql } from "@apollo/client";

const client = new ApolloClient({
  uri: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql"
});

interface TokenActivity {
  type: string;
  fromAddress: string;
  toAddress: string;
  tokenDataId: string;
  timestamp: string;
  tokenName: string;
  collectionName: string;
}

async function getActivityFeed(address: string, limit: number = 50): Promise<TokenActivity[]> {
  const { data } = await client.query({
    query: gql`
      query ActivityFeed($address: String!, $limit: Int!) {
        token_activities_v2(
          where: {
            _or: [
              { from_address: { _eq: $address } },
              { to_address: { _eq: $address } }
            ]
          },
          order_by: { transaction_version: desc },
          limit: $limit
        ) {
          type
          from_address
          to_address
          token_data_id
          transaction_timestamp
          token_data {
            token_name
            current_collection {
              collection_name
            }
          }
        }
      }
    `,
    variables: { address, limit }
  });

  return data.token_activities_v2.map((activity: any) => ({
    type: activity.type,
    fromAddress: activity.from_address,
    toAddress: activity.to_address,
    tokenDataId: activity.token_data_id,
    timestamp: activity.transaction_timestamp,
    tokenName: activity.token_data?.token_name || "Unknown",
    collectionName: activity.token_data?.current_collection?.collection_name || "Unknown"
  }));
}

// Format activity for display
function formatActivity(activity: TokenActivity): string {
  if (activity.type.includes("Mint")) {
    return `Minted ${activity.tokenName}`;
  } else if (activity.type.includes("Transfer")) {
    return `Transferred ${activity.tokenName} from ${activity.fromAddress.substring(0, 6)}... to ${activity.toAddress.substring(0, 6)}...`;
  } else if (activity.type.includes("Burn")) {
    return `Burned ${activity.tokenName}`;
  }
  return `${activity.type} - ${activity.tokenName}`;
}
```

## Advanced Queries

### Collection Trading Volume

```graphql
query TradingVolume($collection_id: String!, $since: timestamp!) {
  token_activities_v2_aggregate(
    where: {
      token_data: {
        collection_id: { _eq: $collection_id }
      },
      type: { _in: ["marketplace_sale", "0x4::token::TransferEvent"] },
      transaction_timestamp: { _gte: $since }
    }
  ) {
    aggregate {
      count
    }
  }

  activities: token_activities_v2(
    where: {
      token_data: {
        collection_id: { _eq: $collection_id }
      },
      type: { _in: ["marketplace_sale"] },
      transaction_timestamp: { _gte: $since }
    },
    order_by: { transaction_timestamp: desc }
  ) {
    transaction_timestamp
    from_address
    to_address
    token_data_id
  }
}
```

### Most Active Traders

```graphql
query ActiveTraders($collection_id: String!, $days: Int!) {
  token_activities_v2(
    where: {
      token_data: {
        collection_id: { _eq: $collection_id }
      },
      transaction_timestamp: { _gte: "now() - ${days} days" }
    },
    distinct_on: from_address
  ) {
    from_address
  }
}
```

### Token Provenance Chain

```graphql
query TokenHistory($token_data_id: String!) {
  token_activities_v2(
    where: { token_data_id: { _eq: $token_data_id } },
    order_by: { transaction_version: asc }
  ) {
    type
    from_address
    to_address
    transaction_version
    transaction_timestamp
    event_account_address
  }
}
```

## Activity Type Classification

Common activity types and their meanings:

- `0x4::token::MintEvent`: NFT was minted
- `0x4::token::TransferEvent`: NFT was transferred
- `0x4::token::BurnEvent`: NFT was burned/destroyed
- `marketplace_listing`: NFT was listed for sale
- `marketplace_sale`: NFT was sold
- `marketplace_delist`: Listing was cancelled

## Pagination Pattern

```typescript
async function getPaginatedActivities(
  address: string,
  limit: number,
  offset: number
): Promise<TokenActivity[]> {
  const { data } = await client.query({
    query: gql`
      query PaginatedActivities($address: String!, $limit: Int!, $offset: Int!) {
        token_activities_v2(
          where: { owner_address: { _eq: $address } },
          order_by: { transaction_version: desc },
          limit: $limit,
          offset: $offset
        ) {
          # fields...
        }
      }
    `,
    variables: { address, limit, offset }
  });

  return data.token_activities_v2;
}
```

## Related Concepts

- [Fungible Assets](https://www.dwellir.com/docs/aptos/fungible_assets) - Token balance queries
- [Aggregations](https://www.dwellir.com/docs/aptos/aggregations) - Activity statistics
- [Subscriptions](https://www.dwellir.com/docs/aptos/subscriptions) - Real-time activity streams
- [Object Model](https://www.dwellir.com/docs/aptos/object_model) - NFT architecture

---

## transactions_batch

# transactions_batch

## Overview

Submit multiple signed transactions in a single API call for improved throughput and reduced latency. Batch submission is ideal for applications that need to submit many transactions quickly, such as airdrops, batch payments, or high-frequency trading operations.

## Endpoint

`POST /v1/transactions/batch`

## Request

\###Request Body
Array of signed transaction objects:

```json
[
  {
    "sender": "0x...",
    "sequence_number": "1",
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": "1234567890",
    "payload": {
      "type": "entry_function_payload",
      "function": "0x1::aptos_account::transfer",
      "type_arguments": ["0x1::aptos_coin::AptosCoin"],
      "arguments": ["0x2", "1000"]
    },
    "signature": {...}
  }
]
```

## Response

### Success Response (202)

Returns array of transaction hashes or failure details:

```json
{
  "transaction_failures": []
}
```

Individual transactions may fail while others succeed. Check response details for each transaction status.

### Error Responses

| Status | Error Code          | Description                            |
| ------ | ------------------- | -------------------------------------- |
| 400    | invalid\_input      | Malformed batch or transaction objects |
| 413    | payload\_too\_large | Batch exceeds size limits              |

## Code Examples

```bash
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/batch" \
  -H "Content-Type: application/json" \
  -d '[{"sender":"0x...","payload":{"type":"entry_function_payload","function":"0x1::aptos_account::transfer","type_arguments":["0x1::aptos_coin::AptosCoin"],"arguments":["0x2","1000"]}}]'
```

Python batch submission:

```python
transactions = [build_transaction(recipient) for recipient in recipients]
response = client.submit_batch_transactions(transactions)
```

## Use Cases

Batch submission provides significant advantages for several scenarios:

1. **Airdrops**: Distribute tokens to hundreds or thousands of addresses efficiently by batching transfer transactions.

2. **Payroll Systems**: Process employee payments or rewards in bulk with a single API interaction.

3. **DEX Operations**: Submit multiple swap or liquidity provision transactions together for atomic execution or improved throughput.

4. **NFT Minting**: Batch mint operations for collections, reducing API overhead and improving deployment speed.

5. **Gaming Rewards**: Distribute in-game rewards or achievements to multiple players efficiently.

6. **Multi-Account Operations**: Execute operations across multiple accounts you control with coordinated submission.

## Best Practices

**Batch Size**: Keep batches under 100 transactions for optimal performance and reliability. Larger batches risk timeouts or partial failures.

**Sequence Number Management**: Ensure sequence numbers are correct and contiguous for transactions from the same sender. Gaps or duplicates cause failures.

**Gas Settings**: Set realistic gas limits for each transaction. One under-gased transaction doesn't affect others.

**Error Handling**: Implement retry logic for failed transactions within a batch. Successful transactions won't be resubmitted.

**Atomicity**: Batch submission doesn't guarantee atomic execution. Transactions execute independently and some may fail while others succeed.

**Rate Limits**: Batching counts toward API rate limits based on total transaction count, not API calls.

## Performance Considerations

Batch submission reduces HTTP overhead from N requests to 1 request for N transactions. This provides 5-10x throughput improvement for large batches compared to individual submission.

Processing time scales with batch size: 100 transactions typically process in 200-500ms total versus 10-30 seconds for individual submissions.

However, batched transactions still enter the mempool individually and execute in separate blocks based on gas price and network conditions.

---

## transactions_by_hash

# transactions_by_hash

## Overview

Retrieve a transaction by its unique hash. This is the primary method for looking up transactions when users provide transaction IDs, typically after submission or from block explorers. The response includes full execution results, events emitted, and gas consumption details.

## Endpoint

`GET /v1/transactions/by_hash/{txn_hash}`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
      -H "Accept: application/json"
```

## Response Fields

- `version` (`string, required`): Ledger version where the transaction was committed
- `hash` (`string, required`): Transaction hash (matches the request parameter)
- `success` (`boolean, required`): Whether the Move VM executed the transaction successfully
- `vm_status` (`string, required`): Execution result detail or error message
- `gas_used` (`string, required`): Actual gas units consumed
- `sender` (`string, required`): Account that submitted and signed the transaction
- `sequence_number` (`string, required`): Sender's sequence number at submission time
- `payload` (`object, required`): The transaction payload (function call details)
- `events` (`array, required`): Events emitted during execution
- `timestamp` (`string, required`): Block timestamp in microseconds since Unix epoch
- `state_change_hash` (`string, required`): Hash of the state changes produced by this transaction
- `event_root_hash` (`string, required`): Merkle root of emitted events

## Successful Response

```json
{
  "version": "123456789",
  "hash": "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383",
  "state_change_hash": "0x...",
  "event_root_hash": "0x...",
  "state_checkpoint_hash": "0x...",
  "gas_used": "8",
  "success": true,
  "vm_status": "Executed successfully",
  "accumulator_root_hash": "0x...",
  "sender": "0xabc...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000000",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "type_arguments": [],
    "arguments": ["0x2", "1000000"]
  },
  "signature": {
    "type": "ed25519_signature",
    "public_key": "0x...",
    "signature": "0x..."
  },
  "events": [
    {
      "guid": { "creation_number": "3", "account_address": "0xabc..." },
      "sequence_number": "15",
      "type": "0x1::coin::WithdrawEvent",
      "data": { "amount": "1000000" }
    }
  ],
  "timestamp": "1700000000000000"
}
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid hash format (must be 0x + 64 hex characters)

### Error 2

- Code: `transaction_not_found`
- Description: Transaction hash is unknown to the node and not present in the current mempool view

## Code Examples

cURL
Python
TypeScript
Rust

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

txn = client.transaction_by_hash(
    "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383"
)

print(f"Success: {txn['success']}")
print(f"Gas used: {txn['gas_used']}")
print(f"VM status: {txn['vm_status']}")
print(f"Version: {txn['version']}")

# Parse events
for event in txn.get("events", []):
    print(f"  Event: {event['type']} - {event['data']}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

const txn = await aptos.getTransactionByHash({
  transactionHash: "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383"
});
console.log(`Status: ${txn.vm_status}`);
console.log(`Success: ${txn.success}`);
console.log(`Gas used: ${txn.gas_used}`);
console.log(`Events: ${txn.events?.length}`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let txn = client.get_transaction_by_hash(
    "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383".parse()?
).await?;

println!("Success: {}, Gas: {}", txn.inner().success, txn.inner().gas_used);
```

## Use Cases

1. **Transaction Confirmation**: After submitting a transaction, poll by hash to check execution status and confirm success or failure before updating application state.

2. **Receipt Generation**: Retrieve full transaction details including events, gas usage, and state changes to generate user-facing receipts and confirmation screens.

3. **Block Explorer Links**: Enable users to click transaction hashes in your UI and view complete transaction details, including payload, events, and gas consumption.

4. **Debugging Failed Transactions**: When transactions fail, retrieve the full transaction object to inspect `vm_status` and understand why execution failed (insufficient balance, abort codes, out of gas, etc.).

5. **Event Processing**: Extract emitted events from specific transactions to update application state, trigger notifications, or maintain off-chain indexes.

6. **Audit Logging**: Store transaction hashes in your database, then retrieve full details on-demand for audit trails without storing complete transaction data locally.

## Understanding vm\_status

The `vm_status` field contains the execution result. Common values:

| vm\_status                                                | Meaning                                      |
| --------------------------------------------------------- | -------------------------------------------- |
| `Executed successfully`                                   | Transaction completed without errors         |
| `Move abort in 0x1::coin: EINSUFFICIENT_BALANCE(0x10006)` | Move abort with error code                   |
| `Out of gas`                                              | Transaction ran out of gas before completing |
| `SEQUENCE_NUMBER_TOO_OLD`                                 | Sequence number was already used             |
| `TRANSACTION_EXPIRED`                                     | Transaction expired before execution         |

When `success` is `false`, the `vm_status` provides the specific error. Move abort codes are module-specific -- check the module's source code to decode the error constant name.

## Best Practices

**Hash Format**: Transaction hashes must be 0x-prefixed 64-character hex strings (32 bytes). Validate format before querying to avoid 400 errors.

**Pending Transactions**: Newly submitted transactions can return `200` with `type: "pending_transaction"` while they are still in the mempool. Use `wait_by_hash` or poll this endpoint until the response changes to a committed transaction payload.

**Caching**: Transaction data is immutable once committed. Cache successfully retrieved transactions indefinitely to minimize API calls. Only pending transactions (404 responses) need re-fetching.

**Sequence vs Hash**: If you know the transaction version (from block data or event streams), use `/transactions/by_version` for faster lookups. Hash lookups use a secondary index and are slightly slower.

**Error Interpretation**: Always check both `success` and `vm_status` fields. A transaction can be committed (not 404) but fail at the application level (`success: false`). Failed transactions still consume gas.

**Event Parsing**: Events in the response follow the same structure as the events endpoints. Use the `type` field to filter for events relevant to your application.

## Performance Considerations

Hash lookups use indexed storage and typically complete in 50-100ms. This is slightly slower than version-based lookups because hash indexes are secondary indexes in the transaction database.

Transaction objects can be large (10KB-1MB) depending on payload size, number of events, and state changes. Complex smart contract interactions with many events generate the largest responses.

For bulk transaction retrieval, use version-based queries or the transaction list endpoint with pagination instead of individual hash lookups, as they provide better database locality and throughput.

## Related Endpoints

- `/v1/transactions/by_version/{version}` - Faster lookup when version is known
- `/v1/transactions/wait_by_hash/{hash}` - Block until transaction is confirmed
- `/v1/transactions` - Submit a new transaction
- `/v1/transactions/simulate` - Test a transaction before submitting
- `/v1/accounts/{address}/transactions` - List all transactions for an account

---

## transactions_by_version

# transactions_by_version

## Overview

Retrieve a transaction by its ledger version number. This is the fastest transaction query method because versions are the primary index for transaction storage. Versions are assigned sequentially to every transaction on the blockchain, providing a total ordering of all state changes in Aptos.

## Endpoint

`GET /v1/transactions/by_version/{txn_version}`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_version/123456789" \
      -H "Accept: application/json"
```

## Response Fields

- `version` (`string, required`): Ledger version (matches the request parameter)
- `hash` (`string, required`): Unique transaction hash
- `success` (`boolean, required`): Whether the Move VM executed successfully
- `vm_status` (`string, required`): Execution result or error details
- `gas_used` (`string, required`): Actual gas units consumed
- `sender` (`string, required`): Account that submitted the transaction
- `sequence_number` (`string, required`): Sender's sequence number at submission
- `payload` (`object, required`): Transaction payload (function call, script, etc.)
- `events` (`array, required`): Events emitted during execution
- `timestamp` (`string, required`): Block timestamp in microseconds since Unix epoch
- `state_change_hash` (`string, required`): Hash of state changes produced
- `accumulator_root_hash` (`string, required`): Root hash of the transaction accumulator

## Successful Response

```json
{
  "version": "123456789",
  "hash": "0x0985b017...",
  "state_change_hash": "0x...",
  "event_root_hash": "0x...",
  "state_checkpoint_hash": "0x...",
  "gas_used": "8",
  "success": true,
  "vm_status": "Executed successfully",
  "accumulator_root_hash": "0x...",
  "sender": "0xabc...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000000",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "type_arguments": [],
    "arguments": ["0x2", "1000000"]
  },
  "signature": {
    "type": "ed25519_signature",
    "public_key": "0x...",
    "signature": "0x..."
  },
  "events": [
    {
      "guid": { "creation_number": "3", "account_address": "0xabc..." },
      "sequence_number": "15",
      "type": "0x1::coin::WithdrawEvent",
      "data": { "amount": "1000000" }
    }
  ],
  "timestamp": "1700000000000000"
}
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid version format (must be a non-negative integer)

### Error 2

- Code: `transaction_not_found`
- Description: Version does not exist yet or has been pruned (before `oldest_ledger_version`)

## Code Examples

cURL
Python
TypeScript
Rust

```bash
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_version/123456789" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Fetch a single transaction by version
txn = client.transaction_by_version(123456789)
print(f"Hash: {txn['hash']}")
print(f"Sender: {txn['sender']}")
print(f"Success: {txn['success']}")
print(f"Gas used: {txn['gas_used']}")

# Process events from the transaction
for event in txn.get("events", []):
    print(f"  Event: {event['type']} - {event['data']}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

const txn = await aptos.getTransactionByVersion({
  ledgerVersion: 123456789n
});
console.log(`Hash: ${txn.hash}`);
console.log(`Gas used: ${txn.gas_used}`);
console.log(`Events: ${txn.events?.length}`);
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let txn = client.get_transaction_by_version(123456789).await?;
println!("Hash: {}", txn.inner().hash);
println!("Success: {}", txn.inner().success);
```

## Version Number Properties

Transaction versions have important properties that make them ideal for indexing and state management:

| Property                 | Description                                                                 |
| ------------------------ | --------------------------------------------------------------------------- |
| Globally unique          | Each version appears exactly once across the entire blockchain              |
| Monotonically increasing | Versions always increase; there are no decreases or resets                  |
| No gaps                  | Every integer from 0 to the current version has a corresponding transaction |
| Sequential within blocks | Each block contains a contiguous range of versions                          |
| Starts at 0              | The genesis transaction has version 0                                       |
| Deterministic ordering   | Versions define the canonical order of all state changes                    |

These properties make versions ideal for checkpointing, streaming, and state machine replication. You can always resume processing from any version without missing transactions.

## Use Cases

1. **Sequential Processing**: Iterate through all transactions by incrementing version numbers, ensuring complete coverage with no missed transactions. This is the foundation of most indexing pipelines.

2. **Block Transaction Retrieval**: After getting a block with its version range (`first_version` to `last_version`), fetch individual transactions within that range for detailed processing.

3. **Event Source Lookup**: When an event references a version number, use this endpoint to retrieve the full transaction that emitted the event, including payload, sender, and other events.

4. **Historical Analysis**: Access transactions at specific points in blockchain history using version numbers as stable, permanent references.

5. **State Reconstruction**: Replay transactions in version order to reconstruct the state of any account or module at any historical point.

6. **Checkpoint Recovery**: After a processing interruption, resume from the last processed version number without risk of duplicate or missed transactions.

## Best Practices

**Version Range Awareness**: Check `GET /v1` (ledger info) to get `oldest_ledger_version` and `ledger_version` before querying. Versions outside this range return 404 errors.

**Version vs Hash**: Use version queries when you have version numbers (from blocks, events, or sequential processing). Use hash queries when users provide transaction IDs. Version queries are approximately 30-50% faster.

**Sequential Access Patterns**: When processing many sequential transactions, version-based queries provide significantly better database locality than hash-based queries, resulting in faster response times.

**Bulk Processing**: For processing large version ranges, use the `GET /v1/transactions?start={version}&limit=100` pagination endpoint instead of individual version queries. Fetching 100 transactions in one call is faster than 100 individual requests.

**Immutability and Caching**: Transaction data at a specific version never changes. Cache historical transactions (older than the latest few hundred versions) indefinitely to minimize API load. Only very recent versions might be affected by reorgs (extremely rare on Aptos).

**404 Disambiguation**: A 404 can mean the version is too new (not yet processed) or too old (pruned). Compare the requested version against `oldest_ledger_version` and `ledger_version` from ledger info to determine the cause.

**System Transaction Handling**: Some versions correspond to system transactions (block\_metadata, state\_checkpoint) rather than user transactions. These have no `sender` or `signature` fields. Handle both types in your processing logic.

## Performance Considerations

Version-based lookups are the fastest transaction query method, typically completing in 30-80ms. They use the primary index on transaction storage, providing optimal I/O patterns and minimal database overhead.

Response sizes match other transaction queries (2KB-1MB depending on complexity). The reduced query latency compared to hash lookups makes version-based access the preferred method for high-throughput indexing pipelines.

For sequential processing of large version ranges, the transaction streaming API (gRPC) provides substantially better throughput than individual REST queries. However, for spot-checking specific versions or low-volume access, the REST endpoint is simpler to integrate.

Performance comparison of query methods:

| Method           | Typical Latency        | Index Type      |
| ---------------- | ---------------------- | --------------- |
| By version       | 30-80ms                | Primary index   |
| By hash          | 50-100ms               | Secondary index |
| List (paginated) | 100-200ms per 100 txns | Sequential scan |

## Related Endpoints

- `/v1/transactions/by_hash/{hash}` - Look up by hash when version is not known
- `/v1/transactions` - List transactions with pagination for bulk retrieval
- `/v1/blocks/by_version/{version}` - Get the block containing this version
- `/v1/blocks/by_height/{height}` - Get block by height (includes version range)
- `/v1` - Get current and oldest available versions

---

## transactions_encode

# transactions_encode

## Overview

Encode a transaction into BCS (Binary Canonical Serialization) bytes ready for signing. This endpoint is the bridge between transaction construction and offline signing workflows. It takes a complete transaction object (without signature) and returns the canonical byte representation that must be signed to produce a valid signed transaction.

## Endpoint

`POST /v1/transactions/encode_submission`

## Request Parameters

- `sender` (`string, required`): Request Body: 0x-prefixed account address of the signer
- `sequence_number` (`string, required`): Request Body: Next sequence number for the sender account
- `max_gas_amount` (`string, required`): Request Body: Maximum gas units the sender is willing to pay
- `gas_unit_price` (`string, required`): Request Body: Price per gas unit in octas
- `expiration_timestamp_secs` (`string, required`): Request Body: Unix timestamp after which the transaction expires
- `payload` (`object, required`): Request Body: Transaction payload (entry_function_payload, script_payload, etc.)

## Request Example

```json
{
  "sender": "0x1a2b3c...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000120",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "arguments": ["0x2a3b4c...", "1000000"]
  }
}
```

## Response Fields

- `result` (`OBJECT, required`): ### Success Response (200) Returns the BCS-encoded transaction bytes as a hex string: ```json "0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193..." ``` These bytes represent the signing message. Sign them with the sender's private key to produce a valid signature. ### Error Responses | Status | Error Code | Description | | -- | -- | -- | | 400 | invalid_input | Invalid transaction format or missing required fields | | 400 | invalid_payload | Payload doesn't conform to entry function or script requirements | | 400 | function_not_found | The specified Move function does not exist on-chain | | 400 | type_error | Type arguments don't match the function signature |

## Successful Response

```json
"0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193..."
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid transaction format or missing required fields

### Error 2

- Code: `invalid_payload`
- Description: Payload doesn't conform to entry function or script requirements

### Error 3

- Code: `function_not_found`
- Description: The specified Move function does not exist on-chain

### Error 4

- Code: `type_error`
- Description: Type arguments don't match the function signature

## Code Examples

cURL
Python
TypeScript
Rust

```bash
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/encode_submission" \
  -H "Content-Type: application/json" \
  -d '{
    "sender": "0x1a2b3c...",
    "sequence_number": "42",
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": "1700000120",
    "payload": {
      "type": "entry_function_payload",
      "function": "0x1::aptos_account::transfer",
      "arguments": ["0x2a3b4c...", "1000000"]
    }
  }'
```

```python
from aptos_sdk.client import RestClient
from aptos_sdk.account import Account

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
account = Account.load_key("your_private_key_hex")

# Step 1: Build transaction object
raw_txn = {
    "sender": str(account.address()),
    "sequence_number": str(client.account_sequence_number(account.address())),
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": str(int(time.time()) + 120),
    "payload": {
        "type": "entry_function_payload",
        "function": "0x1::aptos_account::transfer",
        "arguments": ["0x2a3b4c...", "1000000"]
    }
}

# Step 2: Encode for signing
encoded = client.encode_submission(raw_txn)

# Step 3: Sign the encoded bytes
to_sign = bytes.fromhex(encoded[2:])  # Remove 0x prefix
signature = account.sign(to_sign)

# Step 4: Submit with signature
raw_txn["signature"] = {
    "type": "ed25519_signature",
    "public_key": str(account.public_key()),
    "signature": str(signature)
}
result = client.submit_transaction(raw_txn)
```

```typescript
import { Aptos, AptosConfig, Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// The TypeScript SDK handles encoding internally
// For manual encoding via REST:
const response = await fetch(
  "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/encode_submission",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      sender: "0x1a2b3c...",
      sequence_number: "42",
      max_gas_amount: "2000",
      gas_unit_price: "100",
      expiration_timestamp_secs: String(Math.floor(Date.now() / 1000) + 120),
      payload: {
        type: "entry_function_payload",
        function: "0x1::aptos_account::transfer",
        arguments: ["0x2a3b4c...", "1000000"]
      }
    })
  }
);
const encodedHex = await response.json();
// Sign encodedHex with your signing mechanism
```

```rust
use aptos_sdk::rest_client::Client;
use serde_json::json;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let txn = json!({
    "sender": "0x1a2b3c...",
    "sequence_number": "42",
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": "1700000120",
    "payload": {
        "type": "entry_function_payload",
        "function": "0x1::aptos_account::transfer",
        "arguments": ["0x2a3b4c...", "1000000"]
    }
});

let encoded = client.encode_submission(&txn).await?;
// Sign the encoded bytes with your key management system
```

## Encoding Workflow

The complete encode-sign-submit workflow:

```
1. GET /v1/accounts/{address}          → sequence_number
2. GET /v1/estimate_gas_price          → gas_unit_price
3. Build transaction JSON              → raw transaction object
4. POST /v1/transactions/encode_submission → BCS-encoded bytes
5. Sign encoded bytes (offline)        → signature
6. POST /v1/transactions               → submit signed transaction
7. GET /v1/transactions/by_hash/{hash} → confirm execution
```

This separation enables secure signing workflows where the signing key never touches a networked machine.

## Use Cases

1. **Offline Signing**: Encode transactions on an online machine, transfer encoded bytes to an air-gapped signing device, then return signatures for submission. This is the gold standard for securing high-value operations.

2. **Hardware Wallet Integration**: Send encoded transaction bytes to hardware wallets (Ledger, Trezor) for signing, maintaining security while using blockchain APIs for transaction construction.

3. **Multi-Step Approval Workflows**: Separate transaction construction, review, approval, and signing into distinct steps for organizational governance. Each step can be handled by different systems or personnel.

4. **Cross-Platform Signing**: Encode on a server (web API) and sign on a different platform (mobile app, desktop wallet) without requiring the full Aptos SDK on both sides.

5. **Transaction Inspection**: Encode transactions to verify their exact on-chain representation before signing. Compare encoded bytes against expected values for audit and verification.

6. **Custodial Architectures**: In custodial wallet systems, the hot system builds and encodes transactions while the cold signing system only handles encoded bytes and produces signatures.

## Best Practices

**Complete Transaction Fields**: Include all required fields: sender, sequence\_number, max\_gas\_amount, gas\_unit\_price, expiration\_timestamp\_secs, and payload. Missing fields cause encoding failures.

**Sequence Number Management**: Fetch the latest sequence number from `GET /v1/accounts/{address}` immediately before encoding to avoid conflicts with concurrent transactions.

**Expiration Timestamps**: Set `expiration_timestamp_secs` to current time + 60-120 seconds. Factor in the time needed for the signing round trip when using offline signing -- add extra time for hardware wallet interactions.

**Chain ID Verification**: The encoding implicitly uses the node's chain ID. Ensure you are encoding against the correct network (mainnet vs testnet) to prevent cross-network issues.

**Byte Handling**: The returned hex string is 0x-prefixed. Remove the `0x` prefix before converting to byte arrays for signing. Most signing libraries expect raw bytes, not hex strings.

**Encoding Validation**: After encoding, optionally decode the bytes client-side to verify correctness before sending to signing devices. This catches errors before they reach expensive hardware signing steps.

## Security Considerations

Encoding itself does not involve cryptographic keys or sensitive data. However, the encoded bytes represent a transaction ready for signing -- treat them as sensitive in the following ways:

- **Verify before signing**: Always display or log the human-readable transaction details alongside encoded bytes so signers can verify what they are approving.
- **Secure transport**: When transmitting encoded bytes to signing devices, use authenticated channels to prevent man-in-the-middle modification.
- **Tamper detection**: Any modification to the encoded bytes produces a different signature that will be rejected by the network, providing built-in tamper evidence.

## Performance Considerations

Encoding is a fast, computational operation that validates the transaction against on-chain state (verifying the function exists, types match, etc.) but does not execute it. Response times are typically 20-50ms, dominated by network latency rather than encoding overhead.

Encoded transaction sizes vary:

- Simple transfers: 100-200 bytes
- Smart contract calls: 300-1,000 bytes
- Complex multi-agent transactions: 1-5KB

These sizes are negligible for modern networks but matter for hardware wallet display limitations and QR code encoding use cases.

## Related Endpoints

- `/v1/transactions` - Submit the signed transaction
- `/v1/transactions/simulate` - Simulate before encoding and signing
- `/v1/accounts/{address}` - Get current sequence number
- `/v1/estimate_gas_price` - Get gas price for transaction building
- `/v1/transactions/by_hash/{hash}` - Check submitted transaction status

---

## transactions_list

# transactions_list

## Overview

List transactions from the Aptos blockchain ordered by ledger version. Supports cursor-based pagination via the `start` parameter, making it suitable for sequential processing, indexing pipelines, and building block explorers. Returns transactions across all accounts, not filtered by sender.

## Endpoint

`GET /v1/transactions`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# Get the 25 most recent transactions
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions?limit=25" \
      -H "Accept: application/json"

    # Get transactions starting from a specific version
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions?start=100000000&limit=100" \
      -H "Accept: application/json"
```

## Response Fields

- `version` (`string, required`): Unique, sequential ledger version number
- `hash` (`string, required`): Transaction hash
- `success` (`boolean, required`): Whether execution succeeded
- `vm_status` (`string, required`): Execution result or error message
- `gas_used` (`string, required`): Actual gas consumed
- `sender` (`string, required`): Account that submitted the transaction
- `payload` (`object, required`): The transaction payload
- `events` (`array, required`): Events emitted during execution
- `timestamp` (`string, required`): Block timestamp in microseconds

## Successful Response

```json
[
  {
    "version": "123456789",
    "hash": "0x0985b017...",
    "state_change_hash": "0x...",
    "event_root_hash": "0x...",
    "gas_used": "8",
    "success": true,
    "vm_status": "Executed successfully",
    "accumulator_root_hash": "0x...",
    "sender": "0xabc...",
    "sequence_number": "42",
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": "1700000000",
    "payload": {
      "type": "entry_function_payload",
      "function": "0x1::aptos_account::transfer",
      "type_arguments": [],
      "arguments": ["0x2", "1000000"]
    },
    "events": [...],
    "timestamp": "1700000000000000"
  }
]
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid start version or limit value

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Get the 25 most recent transactions
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions?limit=25" \
  -H "Accept: application/json"

# Get transactions starting from a specific version
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions?start=100000000&limit=100" \
  -H "Accept: application/json"
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Get recent transactions
txns = client.transactions(start=None, limit=25)
for txn in txns:
    print(f"v{txn['version']}: {txn.get('sender', 'system')} - {txn['vm_status']}")

# Paginate through all transactions from a checkpoint
def stream_transactions(client, start_version, batch_size=100):
    version = start_version
    while True:
        batch = client.transactions(start=version, limit=batch_size)
        if not batch:
            break  # Reached chain head
        for txn in batch:
            yield txn
        version = int(batch[-1]["version"]) + 1

for txn in stream_transactions(client, 100_000_000):
    process_transaction(txn)
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Get recent transactions
const txns = await aptos.getTransactions({
  options: { offset: 0, limit: 25 }
});
console.log(`Fetched ${txns.length} transactions`);

// Paginate starting from a specific version
const startVersion = 100_000_000n;
const batch = await aptos.getTransactions({
  options: { offset: Number(startVersion), limit: 100 }
});
for (const txn of batch) {
  console.log(`v${txn.version}: ${txn.success ? "OK" : "FAIL"}`);
}
```

```rust
use aptos_sdk::rest_client::Client;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

// Fetch a batch of transactions
let txns = client.get_transactions(Some(100_000_000), Some(100)).await?;
for txn in txns.inner() {
    println!("Version {}: {}", txn.version, txn.vm_status);
}
```

## Pagination Guide

This endpoint uses version-based cursor pagination:

1. **First page**: Omit `start` to get the most recent transactions, or specify a version to start from a specific point.
2. **Next page**: Take the `version` of the last transaction in the response, add 1, and use it as `start`.
3. **End detection**: An empty array or fewer results than `limit` means you have reached the current chain head.

```python
# Complete pagination pattern
start_version = 0
while True:
    txns = client.transactions(start=start_version, limit=100)
    if not txns:
        # Reached chain head - wait and retry for real-time tailing
        time.sleep(5)
        continue
    for txn in txns:
        process(txn)
    start_version = int(txns[-1]["version"]) + 1
```

Transactions are contiguous with no version gaps. If you encounter missing versions, it indicates a processing error that needs investigation.

## Use Cases

1. **Blockchain Explorers**: Display recent transactions on the homepage or activity feed, showing the latest network activity with filtering and search.

2. **Indexing Pipelines**: Sequentially process all transactions from a starting point to build custom indexes, analytics databases, or application-specific data stores.

3. **Activity Monitoring**: Poll recent transactions to monitor network activity, detect specific transaction patterns (large transfers, contract deployments), or trigger alerts.

4. **Historical Analysis**: Retrieve historical transaction ranges for data analysis, research, or regulatory compliance requirements.

5. **State Reconstruction**: Replay transactions in order to reconstruct account or contract state at any point in history.

6. **Testing and Debugging**: Fetch recent transactions to understand current network behavior during development and troubleshooting.

## Best Practices

**Optimal Limit Values**: Use `limit=100` for efficient bulk retrieval. Smaller limits require more API calls; larger values hit the 100-transaction maximum.

**Pagination Pattern**: Always use the last transaction's `version + 1` as the next `start` parameter. Never rely on `limit * page` calculations, as system transactions and version numbers are sequential.

**Rate Limiting**: When processing historical data, implement rate limiting (10-20 requests per second) to avoid overwhelming the API and hitting quota limits.

**Cursor Persistence**: Save your current position (start version) frequently. If processing stops, resume from the last successfully processed version rather than restarting from the beginning.

**Empty Response Handling**: An empty array means you have reached the current chain head. Wait 4-5 seconds (one block time) before polling again.

**System Transactions**: The response includes system transactions (block\_metadata, state\_checkpoint) alongside user transactions. Filter by `type` if you only need user transactions.

## Performance Considerations

Transaction list queries are optimized for sequential access patterns. Fetching 100 transactions typically completes in 100-200ms. Response times scale linearly with the limit value.

Total response size depends on transaction complexity:

- Simple transfers: approximately 2KB per transaction
- Smart contract calls: approximately 5-15KB per transaction
- Complex multi-agent transactions: approximately 20-50KB per transaction

For high-throughput processing, consider using the Aptos gRPC streaming API which provides 10-100x better throughput than REST pagination for sequential access to large ranges of transactions.

The default `limit` when not specified is 25 transactions. Always specify explicit limits for production code to ensure consistent behavior across API versions.

## Comparison with Streaming API

For processing large ranges of transactions:

| Feature                | REST Pagination              | gRPC Streaming                     |
| ---------------------- | ---------------------------- | ---------------------------------- |
| Integration complexity | Simple HTTP client           | gRPC client required               |
| Throughput             | 100-500 txns/sec             | 10,000-50,000 txns/sec             |
| Latency                | Per-request round trip       | Continuous stream                  |
| Best for               | Ad-hoc queries, small ranges | Real-time tailing, bulk historical |
| Protocol               | HTTP/JSON                    | gRPC/protobuf                      |

Choose REST for ad-hoc queries and small ranges. Choose gRPC streaming for real-time tailing or bulk historical processing.

## Related Endpoints

- `/v1/transactions/by_hash/{hash}` - Look up a specific transaction by hash
- `/v1/transactions/by_version/{version}` - Look up by version (faster for known versions)
- `/v1/accounts/{address}/transactions` - List transactions for a specific account
- `/v1` - Get current ledger version to determine the latest available data

---

## transactions_simulate

# transactions_simulate

## Overview

Simulate transaction execution without committing changes to the blockchain. This endpoint executes transactions in a sandboxed environment, returning gas costs, execution results, and potential errors without consuming gas or affecting on-chain state.

## Endpoint

`POST /v1/transactions/simulate`

## Request

### Request Body

Submit a transaction object similar to `/transactions` submission, but signatures can be omitted or set to zero-filled placeholders:

```json
{
  "sender": "0x...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1234567890",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "type_arguments": ["0x1::aptos_coin::AptosCoin"],
    "arguments": ["0x2", "1000"]
  }
}
```

## Response

### Success Response (200)

Returns simulation results showing what would happen if the transaction were executed:

```json
{
  "version": "123456789",
  "hash": "0x...",
  "state_change_hash": "0x...",
  "event_root_hash": "0x...",
  "gas_used": "8",
  "success": true,
  "vm_status": "Executed successfully",
  "changes": [...],
  "events": [...],
  "timestamp": "1234567890"
}
```

When `success: false`, inspect `vm_status` for the failure reason.

### Error Responses

| Status | Error Code         | Description                          |
| ------ | ------------------ | ------------------------------------ |
| 400    | invalid\_input     | Malformed transaction or payload     |
| 400    | simulation\_failed | Transaction failed during simulation |

## Code Examples

```bash
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/simulate" \
  -H "Content-Type: application/json" \
  -d '{"sender":"0x...","payload":{"type":"entry_function_payload","function":"0x1::aptos_account::transfer","type_arguments":["0x1::aptos_coin::AptosCoin"],"arguments":["0x2","1000"]}}'
```

Python gas estimation:

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
txn = {...}  # Transaction object
simulation = client.simulate_transaction(txn, sender_account)
print(f"Estimated gas: {simulation[0]['gas_used']}")
print(f"Will succeed: {simulation[0]['success']}")
```

TypeScript example:

```typescript
const simulation = await aptos.transaction.simulate.simple({
  signerPublicKey: account.publicKey,
  transaction: rawTxn
});
console.log(`Gas estimate: ${simulation[0].gas_used}`);
```

## Use Cases

Transaction simulation is critical for building robust applications:

1. **Gas Estimation**: Accurately estimate gas costs before submission to set appropriate `max_gas_amount` and avoid under-provisioning that causes transaction failures.

2. **Pre-Flight Validation**: Verify transactions will succeed before asking users to sign, improving UX by catching errors early and preventing failed transaction costs.

3. **Complex Transaction Testing**: Test multi-step or conditional logic in smart contracts to ensure correct behavior before committing real transactions.

4. **Fee Display**: Show users estimated transaction fees before confirmation, enabling informed consent about transaction costs.

5. **Dry Runs**: Test transaction sequences (batch operations, DEX swaps, liquidations) to verify expected outcomes before execution.

6. **Error Debugging**: When developing smart contracts, simulate transactions to understand why they fail without spending gas on failed attempts.

## Best Practices

**Realistic Parameters**: Use realistic `max_gas_amount` and `gas_unit_price` values for accurate simulation. The VM may behave differently with unrealistic limits.

**Current State**: Simulations execute against current blockchain state. For time-sensitive operations, simulate immediately before submission as state can change rapidly.

**Sequence Numbers**: Use the account's next sequence number from `/v1/accounts/{address}`. Incorrect sequence numbers cause simulation failures.

**Signature Requirements**: Most simulations don't require valid signatures. Use zero-filled signature placeholders to avoid signing overhead.

**Multiple Simulations**: Simulate variants (different gas limits, argument values) to understand transaction behavior under various conditions.

**Expiration Handling**: Set expiration\_timestamp\_secs to a future time that matches intended submission time for accurate replay protection simulation.

**State Changes Inspection**: Examine the `changes` array to understand what state modifications will occur, useful for verifying contract correctness.

## Performance Considerations

Simulations execute the full transaction logic including Move VM execution, making them comparable in cost to actual execution. Response times typically range from 50-500ms depending on transaction complexity.

Simple transfers simulate in 50-100ms, while complex smart contract interactions can take 200-500ms. Simulations are slightly faster than real execution because they skip consensus and signature verification.

For batch gas estimation, simulate transactions in parallel using concurrent API requests rather than sequential simulation to minimize latency.

## Simulation vs Actual Execution

Key differences between simulation and real execution:

- No gas charges apply to simulations
- State changes are rolled back after simulation
- Simulations use current state, execution uses state at ledger version
- Failed simulations don't increment account sequence numbers
- Simulation results show hypothetical state changes that won't persist

Use simulation for cost estimation and validation, but be aware that state changes between simulation and execution can cause different outcomes for state-dependent logic.

---

## transactions_submit

# transactions_submit

## Overview

Submit a signed transaction to the Aptos blockchain for execution. This is the primary endpoint for writing on-chain state, executing Move entry functions, deploying modules, and transferring assets. The transaction must be properly constructed, signed, and BCS-encoded before submission.

## Endpoint

`POST /v1/transactions`

## Request Parameters

- `sender` (`string, required`): Request Body (JSON format): 0x-prefixed account address of the signer
- `sequence_number` (`string, required`): Request Body (JSON format): Next sequence number for the sender account
- `max_gas_amount` (`string, required`): Request Body (JSON format): Maximum gas units the sender is willing to pay
- `gas_unit_price` (`string, required`): Request Body (JSON format): Price per gas unit in octas (1 APT = 100,000,000 octas)
- `expiration_timestamp_secs` (`string, required`): Request Body (JSON format): Unix timestamp after which the transaction expires
- `payload` (`object, required`): Request Body (JSON format): Transaction payload describing the operation
- `signature` (`object, required`): Request Body (JSON format): Cryptographic signature over the transaction

## Request Example

```json
{
  "sender": "0x1a2b3c...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000120",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "type_arguments": [],
    "arguments": ["0x2a3b4c...", "1000000"]
  },
  "signature": {
    "type": "ed25519_signature",
    "public_key": "0x...",
    "signature": "0x..."
  }
}
```

## Response Fields

- `result` (`OBJECT, required`): ### Success Response (202) Returns a pending transaction object. The `202 Accepted` status indicates the transaction entered the mempool but has not yet been executed: ```json { "hash": "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383", "sender": "0x1a2b3c...", "sequence_number": "42", "max_gas_amount": "2000", "gas_unit_price": "100", "expiration_timestamp_secs": "1700000120", "payload": { "type": "entry_function_payload", "function": "0x1::aptos_account::transfer", "type_arguments": [], "arguments": ["0x2a3b4c...", "1000000"] } } ``` Use `GET /v1/transactions/wait_by_hash/{hash}` or poll `GET /v1/transactions/by_hash/{hash}` to wait for execution. ### Error Responses | Status | Error Code | Description | | -- | -- | -- | | 400 | invalid_input | Malformed transaction, missing fields, or invalid types | | 400 | invalid_signature | Signature verification failed | | 400 | sequence_number_too_old | Sequence number already used (transaction was already submitted) | | 400 | sequence_number_too_new | Sequence number is ahead of the account's current sequence | | 400 | transaction_expired | expiration_timestamp_secs is in the past | | 400 | insufficient_balance | Account cannot cover max_gas_amount * gas_unit_price | | 413 | payload_too_large | Transaction exceeds maximum allowed size | | 429 | too_many_requests | Rate limited; retry after a short delay | | 500 | internal_error | Server-side processing error |

## Successful Response

```json
{
  "hash": "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383",
  "sender": "0x1a2b3c...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000120",
  "payload": {
    "type": "entry_function_payload",
    "function": "0x1::aptos_account::transfer",
    "type_arguments": [],
    "arguments": ["0x2a3b4c...", "1000000"]
  }
}
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Malformed transaction, missing fields, or invalid types

### Error 2

- Code: `invalid_signature`
- Description: Signature verification failed

### Error 3

- Code: `sequence_number_too_old`
- Description: Sequence number already used (transaction was already submitted)

### Error 4

- Code: `sequence_number_too_new`
- Description: Sequence number is ahead of the account's current sequence

### Error 5

- Code: `transaction_expired`
- Description: expiration_timestamp_secs is in the past

### Error 6

- Code: `insufficient_balance`
- Description: Account cannot cover max_gas_amount * gas_unit_price

### Error 7

- Code: `payload_too_large`
- Description: Transaction exceeds maximum allowed size

### Error 8

- Code: `too_many_requests`
- Description: Rate limited; retry after a short delay

### Error 9

- Code: `internal_error`
- Description: Server-side processing error

## Transaction Signing Flow

The complete submit workflow requires multiple steps:

1. **Get account info** -- fetch the current `sequence_number` from `GET /v1/accounts/{address}`
2. **Estimate gas** -- call `GET /v1/estimate_gas_price` for current gas pricing
3. **Build the transaction** -- construct the payload with all required fields
4. **Encode for signing** -- use `POST /v1/transactions/encode_submission` or client-side BCS encoding
5. **Sign** -- sign the encoded bytes with the sender's private key (Ed25519 or MultiEd25519)
6. **Submit** -- send the signed transaction to `POST /v1/transactions`
7. **Confirm** -- poll `GET /v1/transactions/by_hash/{hash}` until committed

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Submit a pre-signed JSON transaction
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions" \
  -H "Content-Type: application/json" \
  -d '{
    "sender": "0x1a2b3c...",
    "sequence_number": "42",
    "max_gas_amount": "2000",
    "gas_unit_price": "100",
    "expiration_timestamp_secs": "1700000120",
    "payload": {
      "type": "entry_function_payload",
      "function": "0x1::aptos_account::transfer",
      "type_arguments": [],
      "arguments": ["0x2a3b4c...", "1000000"]
    },
    "signature": {
      "type": "ed25519_signature",
      "public_key": "0x...",
      "signature": "0x..."
    }
  }'
```

```python
from aptos_sdk.client import RestClient
from aptos_sdk.account import Account
from aptos_sdk.transactions import TransactionPayload, EntryFunction
from aptos_sdk.type_tag import TypeTag, StructTag

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")
sender = Account.load_key("your_private_key_hex")

# Build and submit an APT transfer
payload = EntryFunction.natural(
    "0x1::aptos_account",
    "transfer",
    [],
    [
        TransactionArgument("0x2a3b4c...", Serializer.struct),
        TransactionArgument(1_000_000, Serializer.u64),
    ],
)

signed_txn = client.create_bcs_signed_transaction(sender, payload)
tx_hash = client.submit_bcs_transaction(signed_txn)
print(f"Submitted: {tx_hash}")

# Wait for confirmation
client.wait_for_transaction(tx_hash)
```

```typescript
import { Aptos, AptosConfig, Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);
const sender = Account.fromPrivateKey({
  privateKey: new Ed25519PrivateKey("0x...")
});

// Build, sign, and submit an APT transfer
const txn = await aptos.transaction.build.simple({
  sender: sender.accountAddress,
  data: {
    function: "0x1::aptos_account::transfer",
    functionArguments: ["0x2a3b4c...", 1_000_000],
  },
});

const signedTxn = await aptos.transaction.sign({ signer: sender, transaction: txn });
const result = await aptos.transaction.submit.simple({ transaction: txn, senderAuthenticator: signedTxn });
console.log(`Submitted: ${result.hash}`);

// Wait for confirmation
const confirmed = await aptos.waitForTransaction({ transactionHash: result.hash });
console.log(`Success: ${confirmed.success}`);
```

```rust
use aptos_sdk::{
    rest_client::Client,
    types::LocalAccount,
    transaction_builder::TransactionFactory,
};

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let mut sender = LocalAccount::from_private_key("your_private_key", 0)?;

// Build and submit a transfer
let factory = TransactionFactory::new(chain_id);
let txn = factory
    .transfer(recipient_address, 1_000_000)
    .sender(sender.address())
    .sequence_number(sender.sequence_number())
    .build();

let signed = sender.sign_transaction(txn);
let pending = client.submit(&signed).await?;
let confirmed = client.wait_for_transaction(&pending).await?;
println!("Gas used: {}", confirmed.inner().gas_used);
```

## Use Cases

1. **Token Transfers**: Send APT between accounts using `0x1::aptos_account::transfer`, or use other entry functions when you need asset-specific transfer flows.

2. **Smart Contract Interaction**: Call any published Move entry function to interact with DeFi protocols, NFT marketplaces, or custom applications.

3. **Module Deployment**: Publish new Move modules to the blockchain using `module_bundle_payload`, enabling smart contract deployment and upgrades.

4. **Multi-Agent Transactions**: Execute transactions requiring multiple signers, such as atomic swaps or escrow operations.

5. **Governance Voting**: Submit votes on on-chain governance proposals through the appropriate governance module entry functions.

## Best Practices

**Sequence Number Management**: Fetch the latest sequence number immediately before building the transaction. For concurrent submissions from the same account, increment locally and handle `sequence_number_too_new` errors with retries.

**Gas Configuration**: Use `estimate_gas_price` for current pricing. Set `max_gas_amount` higher than expected consumption (simulate first) to avoid out-of-gas failures. Unused gas is not charged.

**Expiration Strategy**: Set `expiration_timestamp_secs` to current time + 60-120 seconds. Shorter expirations reduce the window for stale transactions; longer expirations risk mempool bloat.

**BCS vs JSON**: Use BCS encoding (`application/x.aptos.signed_transaction+bcs`) in production for smaller payloads and faster processing. JSON encoding is useful for debugging and development.

**Idempotency**: Resubmitting the same signed transaction is safe. The node deduplicates by hash in the mempool and rejects already-committed transactions with `sequence_number_too_old`.

**Error Recovery**: On `429` (rate limited), implement exponential backoff. On `sequence_number_too_old`, refresh the sequence number and rebuild. On `insufficient_balance`, check the account balance before retrying.

## Performance Considerations

Transaction submission typically completes in 50-200ms (time to enter the mempool). Actual execution occurs in the next 1-3 blocks (4-12 seconds on mainnet).

Transaction size affects submission time: simple transfers are under 500 bytes, while module deployments can reach the maximum transaction size. Larger payloads take longer to transmit and validate.

For high-throughput scenarios, pipeline submissions by incrementing sequence numbers locally rather than waiting for each transaction to confirm before sending the next.

## Related Endpoints

- `/v1/transactions/encode_submission` - Encode transaction for offline signing
- `/v1/transactions/simulate` - Simulate before submitting
- `/v1/transactions/by_hash/{hash}` - Check transaction status
- `/v1/transactions/wait_by_hash/{hash}` - Wait for confirmation
- `/v1/accounts/{address}` - Get current sequence number
- `/v1/estimate_gas_price` - Current gas pricing

---

## transactions_wait_by_hash

# transactions_wait_by_hash

## Overview

Wait for a transaction to be committed or to expire. This endpoint blocks (long-polls) until the transaction identified by its hash appears on-chain or the server-side wait window elapses. It is the recommended way to confirm transaction execution after submission.

> **Note:** Dwellir Aptos fullnodes do expose `/transactions/wait_by_hash`. Keep a polling fallback only when you need custom retry behavior in your own client.

If you call this endpoint with an unknown hash, the node may return `transaction_not_found` immediately instead of holding the request open for the full timeout window.

## Endpoint

`GET /v1/transactions/wait_by_hash/{txn_hash}`

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
# Native wait (when available)
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/wait_by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
      -H "Accept: application/json"

    # Polling fallback (recommended for shared nodes)
    curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
      -H "Accept: application/json"
```

## Response Fields

- `version` (`string, required`): Ledger version where the transaction was committed
- `hash` (`string, required`): Transaction hash matching the request
- `success` (`boolean, required`): Whether the transaction executed successfully
- `vm_status` (`string, required`): Human-readable execution status or error message
- `gas_used` (`string, required`): Actual gas units consumed
- `events` (`array, required`): Events emitted during execution
- `timestamp` (`string, required`): Block timestamp in microseconds since Unix epoch

## Successful Response

```json
{
  "version": "123456789",
  "hash": "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383",
  "state_change_hash": "0x...",
  "event_root_hash": "0x...",
  "gas_used": "8",
  "success": true,
  "vm_status": "Executed successfully",
  "sender": "0xabc...",
  "sequence_number": "42",
  "max_gas_amount": "2000",
  "gas_unit_price": "100",
  "expiration_timestamp_secs": "1700000000",
  "payload": { "type": "entry_function_payload", "function": "0x1::aptos_account::transfer" },
  "events": [],
  "timestamp": "1700000000000000"
}
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Invalid hash format (must be 0x-prefixed, 64 hex chars)

### Error 2

- Code: `transaction_not_found`
- Description: Transaction hash is unknown to both committed storage and the current mempool view

## Polling Behavior

If you need custom timeout control or want to avoid a single long-held request, implement client-side polling:

1. Submit the transaction via `POST /v1/transactions`
2. Poll `GET /v1/transactions/by_hash/{hash}` at regular intervals
3. A `200` response with `type: "pending_transaction"` means the hash is known but not committed yet
4. A committed transaction also returns `200` -- check `type`, `success`, and `vm_status`
5. Stop polling when `expiration_timestamp_secs` has passed and the transaction has not appeared

Recommended polling interval is 1-2 seconds. Aptos block time is approximately 4 seconds on mainnet, so polling more frequently than once per second provides no benefit.

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Native wait (when available)
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/wait_by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
  -H "Accept: application/json"

# Polling fallback (recommended for shared nodes)
curl -X GET "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/transactions/by_hash/0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383" \
  -H "Accept: application/json"
```

```python
import time
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

def wait_for_transaction(txn_hash: str, timeout_secs: int = 30) -> dict:
    """Poll until a transaction is committed or the client-side timeout expires."""
    deadline = time.time() + timeout_secs
    while time.time() < deadline:
        try:
            txn = client.transaction_by_hash(txn_hash)
            if txn.get("type") == "pending_transaction":
                time.sleep(1.5)
                continue
            return txn
        except Exception:
            pass  # transaction not yet visible, retry
        time.sleep(1.5)
    raise TimeoutError(f"Transaction {txn_hash} not confirmed within {timeout_secs}s")

# Usage after submitting a transaction
result = wait_for_transaction("0x0985b017...")
print(f"Success: {result['success']}, Gas used: {result['gas_used']}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// The SDK has built-in wait functionality
const txn = await aptos.waitForTransaction({
  transactionHash: "0x0985b017...",
  options: { timeoutSecs: 30, checkSuccess: true }
});
console.log(`Confirmed at version ${txn.version}`);

// Manual polling fallback
async function pollTransaction(hash: string, timeoutMs = 30000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    try {
      const txn = await aptos.getTransactionByHash({ transactionHash: hash });
      if (txn.type !== "pending_transaction") return txn;
    } catch {
      // 404 - still pending
    }
    await new Promise(r => setTimeout(r, 1500));
  }
  throw new Error(`Timeout waiting for ${hash}`);
}
```

```rust
use aptos_sdk::rest_client::Client;
use std::time::{Duration, Instant};
use tokio::time::sleep;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);
let hash = "0x0985b0179db0c0971bb9bf331dff7fdae6dacf73cee48a1ec6be614f41d4d383";

// The Rust SDK provides wait_for_transaction
let txn = client.wait_for_transaction_by_hash(
    hash.parse()?,
    Duration::from_secs(30),
).await?;

println!("Success: {}, Version: {}", txn.inner().success, txn.inner().version);
```

## Use Cases

1. **Transaction Confirmation Flow**: After submitting a transfer or smart contract call, wait for on-chain confirmation before updating your application state or notifying the user.

2. **Sequential Transaction Pipelines**: When transactions depend on each other (such as approve then swap on a DEX), wait for each to confirm before submitting the next to ensure correct sequence numbers.

3. **Payment Processing**: In commerce applications, hold the checkout flow until the payment transaction is confirmed on-chain, then release the goods or services.

4. **Multi-Signature Coordination**: After the final signer submits a multi-sig transaction, all parties can wait on the hash to confirm the collective operation succeeded.

5. **Retry Logic**: If a transaction is not confirmed within the expected window, resubmit with a higher gas price or updated expiration to handle network congestion.

## Best Practices

**Client Timeouts**: If you wrap this endpoint with your own retry loop, set your HTTP timeout longer than the node's wait window and keep a separate client-side deadline for the overall confirmation flow.

**Expiration Awareness**: If `expiration_timestamp_secs` has passed and the transaction is not on-chain, it will never be executed. Stop polling and consider resubmitting.

**Success vs Execution**: A confirmed transaction (`200` response) does not guarantee success. Always check the `success` field -- a transaction can be committed but fail at the Move VM level (for example, insufficient balance).

**Idempotency**: Polling `by_hash` is safe to repeat. The same hash always returns the same committed transaction once available.

**Connection Timeouts**: When using the native blocking endpoint, ensure your HTTP client timeout comfortably exceeds the server's wait window so the request is not cut off prematurely.

## Performance Considerations

The native `wait_by_hash` endpoint uses server-side long polling, which is more efficient than client-side polling because it avoids repeated HTTP round trips. Use polling when you need custom retry control or want to multiplex many outstanding waits in your own worker.

With client-side polling at 1.5-second intervals, a typical transaction confirms after 2-3 polls (3-5 seconds). The overhead is minimal: each poll is a lightweight hash-indexed lookup completing in 50-100ms.

For applications processing many concurrent transactions, use parallel polling with a shared connection pool rather than sequential waits to maximize throughput.

## Related Endpoints

- `/v1/transactions` - Submit a signed transaction
- `/v1/transactions/by_hash/{txn_hash}` - Get transaction by hash (non-blocking)
- `/v1/transactions/by_version/{txn_version}` - Get transaction by version
- `/v1/transactions/simulate` - Simulate before submitting

---

## upgradability

# upgradability

Smart contract upgradability is a critical feature on Aptos that allows developers to fix bugs, add features, and improve performance while maintaining the same on-chain address. Move's upgrade system provides powerful flexibility with built-in safety checks to prevent breaking changes that could corrupt existing on-chain state or break dependent contracts.

## Overview

On Aptos, modules can be upgraded by publishing new versions to the same address. The blockchain enforces compatibility rules to ensure that upgrades don't break existing functionality or corrupt stored data. Upgrades can be configured with different policies ranging from fully immutable (no upgrades allowed) to arbitrary (any changes permitted), with compatibility checks as the recommended middle ground.

## Upgrade Policies

Aptos supports three upgrade policies that control what changes are permitted:

```move
// In Move.toml or via CLI
[package]
name = "MyProject"
version = "1.0.0"
upgrade_policy = "compatible"  // Options: immutable, compatible, arbitrary
```

**Immutable**: Once published, the module cannot be upgraded. This provides maximum security and predictability but eliminates flexibility to fix bugs or add features.

**Compatible** (Recommended): Upgrades must maintain backward compatibility. Public functions, struct layouts, and type signatures cannot change in breaking ways, but you can add new functions and internal improvements.

**Arbitrary**: Any changes are permitted including breaking changes to public APIs and struct layouts. Use with extreme caution as this can corrupt on-chain data.

## Compatibility Rules

When using the compatible upgrade policy, the following rules apply:

### Allowed Changes

- Adding new functions (public, entry, or internal)
- Adding new structs and types
- Adding new fields to structs (with care)
- Modifying internal function implementations
- Improving gas efficiency
- Adding new modules to the package

### Prohibited Changes

- Removing public functions
- Changing public function signatures (parameters or return types)
- Removing or reordering existing struct fields
- Changing struct abilities (key, store, copy, drop)
- Removing or renaming modules
- Changing friend declarations that break existing integrations

## Upgrade Process

```bash
# 1. Prepare the upgraded module
# Edit your Move code with compatible changes

# 2. Test the upgrade locally
aptos move test --package-dir .

# 3. Compile with upgrade check
aptos move compile --package-dir . --save-metadata

# 4. Deploy the upgrade
aptos move publish \
  --package-dir . \
  --named-addresses my_project=0xYOUR_ADDRESS \
  --profile mainnet

# 5. Verify the upgrade
aptos move view \
  --function-id 0xYOUR_ADDRESS::module::version \
  --profile mainnet
```

## Version Management

Implement version tracking in your modules:

```move
module 0x1::upgradeable_contract {
    use std::signer;

    const VERSION: u64 = 2; // Increment with each upgrade

    struct Config has key {
        version: u64,
        admin: address,
        // Other config fields
    }

    public entry fun initialize(account: &signer) {
        move_to(account, Config {
            version: VERSION,
            admin: signer::address_of(account)
        });
    }

    // Migration function for upgrades
    public entry fun migrate(admin: &signer) acquires Config {
        let config = borrow_global_mut<Config>(signer::address_of(admin));
        assert!(config.version < VERSION, 1); // Already migrated

        // Perform migration logic
        if (config.version == 1) {
            // Migrate from v1 to v2
            config.version = VERSION;
        };
    }

    #[view]
    public fun get_version(addr: address): u64 acquires Config {
        borrow_global<Config>(addr).version
    }
}
```

## Safe Struct Evolution

When adding fields to structs, ensure backward compatibility:

```move
// Version 1
struct UserProfile has key {
    name: vector<u8>,
    created_at: u64
}

// Version 2 - Safe addition with default handling
struct UserProfile has key {
    name: vector<u8>,
    created_at: u64,
    avatar_url: Option<vector<u8>>  // Use Option for new fields
}

// Migration helper
public fun migrate_profile(user: &signer) acquires UserProfile {
    let profile = borrow_global_mut<UserProfile>(signer::address_of(user));
    // Existing profiles automatically get None for new optional field
}
```

## Real-World Use Cases

1. **Bug Fixes**: Quickly patch security vulnerabilities or logical errors in deployed contracts without changing addresses or disrupting users.

2. **Feature Additions**: Add new functionality to existing protocols like new token types, additional governance mechanisms, or enhanced analytics.

3. **Performance Optimization**: Upgrade contract implementations to use more efficient algorithms or data structures while maintaining the same external interface.

4. **Parameter Tuning**: Adjust economic parameters, fee structures, or limits in DeFi protocols based on real-world usage and market conditions.

5. **Emergency Responses**: Implement circuit breakers, pause functionality, or other emergency measures when security issues are discovered.

6. **Protocol Evolution**: Gradually evolve complex protocols like DEXes or lending platforms by adding new features while maintaining backward compatibility with existing integrations.

## Best Practices

**Always Use Compatible Mode**: Unless you have specific requirements, use the compatible upgrade policy to balance flexibility with safety.

**Version All Modules**: Include version constants in all modules and expose them through view functions for transparency and debugging.

**Test Upgrades Thoroughly**: Test upgraded modules against existing on-chain state before deploying to mainnet. Use testnet to validate migration paths.

**Implement Migration Functions**: Provide explicit migration functions for users to upgrade their stored data when struct layouts change.

**Document Breaking Changes**: Even when using arbitrary mode, clearly document all changes and provide migration guides for users and integrators.

**Use Multi-Sig for Upgrades**: Require multiple signatures for upgrade transactions on production contracts to prevent unauthorized or accidental upgrades.

**Gradual Rollout**: For major upgrades, consider implementing feature flags that allow gradual activation after deployment.

**Preserve Historical Versions**: Keep records of all deployed versions with their code and deployment transactions for audit and recovery purposes.

## Package Manager Pattern

For complex upgrades, consider implementing a package manager:

```move
module 0x1::package_manager {
    struct PackageMetadata has key {
        name: vector<u8>,
        version: u64,
        upgrade_number: u64,
        upgrade_policy: u8,
        source_digest: vector<u8>
    }

    public entry fun record_upgrade(
        publisher: &signer,
        version: u64,
        digest: vector<u8>
    ) acquires PackageMetadata {
        let addr = signer::address_of(publisher);
        if (!exists<PackageMetadata>(addr)) {
            move_to(publisher, PackageMetadata {
                name: b"MyPackage",
                version,
                upgrade_number: 0,
                upgrade_policy: 1, // compatible
                source_digest: digest
            });
        } else {
            let metadata = borrow_global_mut<PackageMetadata>(addr);
            metadata.version = version;
            metadata.upgrade_number = metadata.upgrade_number + 1;
            metadata.source_digest = digest;
        };
    }
}
```

## Related Concepts

- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Design modules for upgradability
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Handle resource migration
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Test upgrade scenarios
- [Formal Verification](https://www.dwellir.com/docs/aptos/formal_verification) - Verify upgrade safety properties

---

## user_transactions

> Coming soon: Need support for this? Email <support@dwellir.com> and we will enable it for you.

# user_transactions

## Overview

Query transactions submitted by a specific address.

## Query Structure

```graphql
query GetUserTransactions($address: String!, $limit: Int) {
  user_transactions(
    where: { sender: { _eq: $address } }
    limit: $limit
    order_by: { version: desc }
  ) {
    version
    hash
    success
    vm_status
    gas_used
  }
}
```

## Variables

| Name    | Type   | Required | Description     |
| ------- | ------ | -------- | --------------- |
| address | String | Yes      | Account address |
| limit   | Int    | No       | Result limit    |

## Code Examples

Python
TypeScript

```
import requests
query = """query GetUserTransactions($address: String!, $limit: Int) { user_transactions(where: { sender: { _eq: $address } }, limit: $limit, order_by: { version: desc }) { version hash success vm_status gas_used } }"""
variables = {"address": "0x1", "limit": 10}
requests.post("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql", json={"query": query, "variables": variables})
```

```
const query = `query GetUserTransactions($address: String!, $limit: Int) { user_transactions(where: { sender: { _eq: $address } }, limit: $limit, order_by: { version: desc }) { version hash success vm_status gas_used } }`;
const variables = { address: "0x1", limit: 10 };
await fetch("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql", { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }) });
```

---

## view

# view

## Overview

Execute a Move view function without submitting a transaction, consuming gas, or requiring a signature. View functions are read-only operations that run in the Move VM and return computed results. They are the most efficient way to query on-chain state that requires computation, such as calculated balances, prices, permissions, or derived values.

Only functions explicitly marked `view` can be called through this endpoint. The target function must actually be marked as view. A regular public function is not enough on its own.

## Endpoint

`POST /v1/view`

## Request Parameters

- `ledger_version` (`string, optional`): Query parameter: Execute the view function against state at this historical version
- `function` (`string, required`): Request Body: Fully qualified function identifier: `address::module::function_name`
- `type_arguments` (`array, required`): Request Body: Type parameters for generic functions (empty array if none needed)
- `arguments` (`array, required`): Request Body: Function arguments as JSON values (strings for addresses, numbers, booleans)

## Request Example

```json
{
  "function": "0x1::coin::balance",
  "type_arguments": ["0x1::aptos_coin::AptosCoin"],
  "arguments": ["0x1"]
}
```

## Response Fields

- `result` (`OBJECT, required`): ### Success Response (200) Returns an array of return values from the function: ```json [ "1500000000" ] ``` Return values are JSON-encoded according to their Move types. Functions returning multiple values produce arrays with multiple elements. ### Error Responses | Status | Error Code | Description | | -- | -- | -- | | 400 | invalid_input | Malformed request body, unknown function, or wrong number of arguments | | 400 | function_not_found | The specified function does not exist at the given address | | 400 | type_error | Type arguments don't match function signature | | 500 | vm_error | Function execution failed in the Move VM (such as arithmetic overflow, abort) |

## Successful Response

```json
[
  "1500000000"
]
```

## Error Responses

### Error 1

- Code: `invalid_input`
- Description: Malformed request body, unknown function, or wrong number of arguments

### Error 2

- Code: `function_not_found`
- Description: The specified function does not exist at the given address

### Error 3

- Code: `type_error`
- Description: Type arguments don't match function signature

### Error 4

- Code: `vm_error`
- Description: Function execution failed in the Move VM (such as arithmetic overflow, abort)

## Code Examples

cURL
Python
TypeScript
Rust

```bash
# Check APT balance
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/view" \
  -H "Content-Type: application/json" \
  -d '{"function":"0x1::coin::balance","type_arguments":["0x1::aptos_coin::AptosCoin"],"arguments":["0x1"]}'

# Check if an account is registered for a coin
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/view" \
  -H "Content-Type: application/json" \
  -d '{"function":"0x1::coin::is_account_registered","type_arguments":["0x1::aptos_coin::AptosCoin"],"arguments":["0x1"]}'

# Query at a historical version
curl -s -X POST "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/view?ledger_version=50000000" \
  -H "Content-Type: application/json" \
  -d '{"function":"0x1::coin::balance","type_arguments":["0x1::aptos_coin::AptosCoin"],"arguments":["0x1"]}'
```

```python
from aptos_sdk.client import RestClient

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Check APT balance
result = client.view_function(
    "0x1::coin::balance",
    ["0x1::aptos_coin::AptosCoin"],
    ["0x1"]
)
balance_octas = int(result[0])
print(f"Balance: {balance_octas / 1e8} APT")

# Check coin registration
registered = client.view_function(
    "0x1::coin::is_account_registered",
    ["0x1::aptos_coin::AptosCoin"],
    ["0x1"]
)
print(f"Registered: {registered[0]}")

# Query total supply
supply = client.view_function(
    "0x1::coin::supply",
    ["0x1::aptos_coin::AptosCoin"],
    []
)
print(f"Supply: {supply[0]}")
```

```typescript
import { Aptos, AptosConfig } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Check APT balance
const [balance] = await aptos.view({
  payload: {
    function: "0x1::coin::balance",
    typeArguments: ["0x1::aptos_coin::AptosCoin"],
    functionArguments: ["0x1"]
  }
});
console.log(`Balance: ${Number(balance) / 1e8} APT`);

// Check if account exists
const [exists] = await aptos.view({
  payload: {
    function: "0x1::account::exists_at",
    typeArguments: [],
    functionArguments: ["0x1"]
  }
});
console.log(`Account exists: ${exists}`);

// Query a custom DeFi protocol
const [price] = await aptos.view({
  payload: {
    function: "0xdex_address::pool::get_price",
    typeArguments: ["0x1::aptos_coin::AptosCoin", "0xusdc_address::usdc::USDC"],
    functionArguments: []
  }
});
```

```rust
use aptos_sdk::rest_client::Client;
use serde_json::json;

let client = Client::new("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1".parse()?);

let result = client.view(
    &json!({
        "function": "0x1::coin::balance",
        "type_arguments": ["0x1::aptos_coin::AptosCoin"],
        "arguments": ["0x1"]
    }),
    None,  // ledger_version
).await?;
println!("Balance: {:?}", result.inner());
```

## Common View Function Patterns

### Balance and Token Queries

```json
// Check coin balance
{ "function": "0x1::coin::balance", "type_arguments": ["0x1::aptos_coin::AptosCoin"], "arguments": ["0x1"] }

// Check if registered for a coin
{ "function": "0x1::coin::is_account_registered", "type_arguments": ["0x1::aptos_coin::AptosCoin"], "arguments": ["0x1"] }

// Get coin decimals
{ "function": "0x1::coin::decimals", "type_arguments": ["0x1::aptos_coin::AptosCoin"], "arguments": [] }

// Get coin name
{ "function": "0x1::coin::name", "type_arguments": ["0x1::aptos_coin::AptosCoin"], "arguments": [] }

// Get coin supply
{ "function": "0x1::coin::supply", "type_arguments": ["0x1::aptos_coin::AptosCoin"], "arguments": [] }
```

### Account and System Queries

```json
// Check if account exists
{ "function": "0x1::account::exists_at", "type_arguments": [], "arguments": ["0x1"] }

// Read a view that is explicitly marked `view`
{ "function": "0x1::account::exists_at", "type_arguments": [], "arguments": ["0x1"] }
```

## Use Cases

1. **Balance Queries**: Check account balances for any fungible token without transaction overhead or gas costs. Ideal for wallet UIs, portfolio trackers, and balance verification.

2. **State Inspection**: Read computed contract state like pool prices, lending rates, or staking rewards that require on-chain calculation rather than simple resource reads.

3. **Validation Checks**: Verify conditions before transaction submission (sufficient balance, correct permissions, valid parameters) to prevent failed transactions and wasted gas.

4. **UI Data Fetching**: Power application UIs with real-time blockchain data. View functions are fast enough for user-facing queries without needing a dedicated indexer.

5. **Price Feeds**: Query DEX pool prices, oracle data, or computed exchange rates for display, trading logic, or arbitrage detection.

6. **Permission Checks**: Verify user roles, capabilities, or access control before allowing operations in your application.

## Best Practices

**Function Visibility**: Only functions explicitly marked with the `#[view]` attribute can be called via this endpoint. Entry functions and ordinary `public fun` functions that are not declared as view functions cannot be used here, even if they only read state.

**Gas-Free Execution**: View functions do not consume gas or require signatures, making them ideal for frequent, high-volume queries from frontends and monitoring systems.

**Type Arguments**: Provide full type paths including generics. For example, use `0x1::aptos_coin::AptosCoin` rather than just `AptosCoin`. Type arguments correspond to the generic parameters in the function signature.

**Argument Encoding**: Arguments are passed as JSON values and automatically BCS-encoded by the API. Large integers (u64 and above) must be passed as strings to avoid JSON precision loss.

**Caching**: View function results reflect the state at the current (or specified) ledger version. Cache based on how frequently the underlying state changes -- token balances change with every transfer, while protocol parameters may change infrequently.

**Ledger Version**: Use the `ledger_version` query parameter to query historical state. This enables time-travel queries and consistent multi-call snapshots when all calls use the same version.

**Error Handling**: View functions that call `abort` in the Move VM return a 500 error. Check for this when querying functions that have assertion checks (such as querying a balance for a non-existent coin store).

## Performance Considerations

View functions execute directly in the Move VM without consensus or state commitment overhead. Response times are typically 20-100ms depending on function complexity.

- Simple reads (balance, existence check): 20-50ms
- Moderate computation (price calculation, aggregate queries): 50-100ms
- Complex computations (multi-step simulations): 100-200ms

View functions are significantly faster than full transaction simulation for read operations. For frequently accessed data, implement client-side caching with TTLs based on block time (approximately 4 seconds on mainnet).

For batch queries, make concurrent requests rather than sequential calls. View functions are stateless and can safely run in parallel without ordering concerns.

## Related Endpoints

- `/v1/accounts/{address}/resource/{resource_type}` - Direct resource read (no computation)
- `/v1/accounts/{address}/resources` - List all resources under an account
- `/v1/accounts/{address}/modules` - Discover available view functions via ABI
- `/v1/transactions/simulate` - Simulate a full transaction with state changes

---

## view_functions

# view_functions

View functions provide gas-free read access to on-chain state, enabling applications to query smart contract data without submitting transactions or paying fees. They are essential for building responsive user interfaces, analytics dashboards, and efficient off-chain systems that need to read blockchain state frequently.

## Overview

View functions in Aptos are special functions marked with the `#[view]` attribute that can be called through the REST API without creating a transaction. They are read-only, cannot modify state, and execute immediately without waiting for block confirmation. This makes them perfect for displaying balances, checking permissions, computing derived values, and providing real-time data to applications.

## Defining View Functions

View functions must be public and marked with the `#[view]` attribute:

```move
module 0x1::token_info {
    use std::signer;
    use std::string::String;

    struct TokenStore has key {
        balance: u64,
        name: String,
        symbol: String,
        decimals: u8
    }

    struct Supply has key {
        total: u64,
        max: u64
    }

    // Simple view function
    #[view]
    public fun balance_of(owner: address): u64 acquires TokenStore {
        if (!exists<TokenStore>(owner)) {
            return 0
        };
        borrow_global<TokenStore>(owner).balance
    }

    // View function with multiple parameters
    #[view]
    public fun allowance(owner: address, spender: address): u64 acquires Allowances {
        // Return approved amount for spender
        0
    }

    // View function returning multiple values
    #[view]
    public fun token_metadata(addr: address): (String, String, u8) acquires TokenStore {
        let store = borrow_global<TokenStore>(addr);
        (store.name, store.symbol, store.decimals)
    }

    // View function with computation
    #[view]
    public fun circulating_supply(): u64 acquires Supply {
        let supply = borrow_global<Supply>(@0x1);
        supply.total
    }

    // View function checking conditions
    #[view]
    public fun is_paused(): bool acquires Config {
        borrow_global<Config>(@0x1).paused
    }

    // View function with vector return
    #[view]
    public fun get_holders(start: u64, limit: u64): vector<address> acquires HolderRegistry {
        // Return list of token holders
        vector::empty()
    }
}
```

## Calling View Functions

### REST API

View functions are called via HTTP POST to the `/v1/view` endpoint:

```bash
curl -X POST https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/view \
  -H "Content-Type: application/json" \
  -d '{
    "function": "0x1::token_info::balance_of",
    "type_arguments": [],
    "arguments": ["0x123abc..."]
  }'
```

Response:

```json
{
  "result": ["1000000"]
}
```

### TypeScript SDK

```typescript
import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";

const config = new AptosConfig({
  network: Network.MAINNET,
  fullnode: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1"
});
const aptos = new Aptos(config);

// Call view function
const balance = await aptos.view({
  payload: {
    function: "0x1::token_info::balance_of",
    typeArguments: [],
    functionArguments: ["0x123abc..."]
  }
});

console.log(`Balance: ${balance[0]}`);

// Call view function with multiple returns
const [name, symbol, decimals] = await aptos.view({
  payload: {
    function: "0x1::token_info::token_metadata",
    typeArguments: [],
    functionArguments: ["0x1"]
  }
});
```

### Python SDK

```python
from aptos_sdk.client import RestClient
from aptos_sdk.account_address import AccountAddress

client = RestClient("https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1")

# Call view function
balance = client.view_function(
    function="0x1::token_info::balance_of",
    type_arguments=[],
    arguments=["0x123abc..."]
)

print(f"Balance: {balance[0]}")
```

## Complex View Functions

### Aggregating Data

```move
#[view]
public fun get_portfolio_value(owner: address): u64 acquires TokenStore, PriceOracle {
    let total_value = 0u64;
    let store = borrow_global<TokenStore>(owner);
    let price = get_token_price(); // Internal helper

    total_value = store.balance * price / 1000000; // Adjust for decimals
    total_value
}

#[view]
public fun get_top_holders(limit: u64): vector<address> acquires HolderRegistry {
    // Query and sort holders by balance
    // Return top N addresses
    vector::empty()
}
```

### Pagination Support

```move
#[view]
public fun get_transactions(
    account: address,
    offset: u64,
    limit: u64
): vector<Transaction> acquires TransactionHistory {
    let history = borrow_global<TransactionHistory>(account);
    // Return paginated results
    vector::empty()
}
```

## Real-World Use Cases

1. **Wallet Displays**: Fetch token balances, NFT collections, and transaction history to display in user wallets without gas costs.

2. **DeFi Dashboards**: Query liquidity pool reserves, staking rewards, lending positions, and APY calculations for real-time financial data.

3. **NFT Marketplaces**: Check NFT ownership, metadata, listing prices, and royalty information to populate marketplace listings efficiently.

4. **Analytics Platforms**: Aggregate protocol metrics like total value locked, trading volumes, user counts, and historical statistics.

5. **Gaming Applications**: Retrieve player stats, inventory items, leaderboard positions, and game state without requiring transactions.

6. **Permission Checks**: Verify user access rights, admin privileges, or whitelist status before displaying UI elements or allowing actions.

## Best Practices

**Keep Computations Light**: View functions execute during API calls, so avoid expensive computations. Consider pre-computing values in transaction functions and storing them.

**Return Meaningful Defaults**: Use defensive programming to return sensible defaults (like 0 for balances) when resources don't exist, avoiding errors in calling code.

**Use Pagination**: For functions returning large datasets, implement offset and limit parameters to prevent timeouts and excessive response sizes.

**Cache Results**: On the application side, cache view function results when appropriate to reduce API calls and improve performance.

**Validate Inputs**: Check that addresses and parameters are valid to provide better error messages than internal assertion failures.

**Document Return Types**: Clearly document what each view function returns, especially when returning multiple values or complex structures.

**Consider Gas Limits**: While view functions don't charge gas, they still have execution limits. Very complex computations may timeout.

## View Functions vs Entry Functions

```move
// Entry function - Modifies state, requires transaction
public entry fun transfer(from: &signer, to: address, amount: u64) acquires TokenStore {
    // Mutates state
    let from_store = borrow_global_mut<TokenStore>(signer::address_of(from));
    from_store.balance = from_store.balance - amount;
    // ...
}

// View function - Read-only, no transaction needed
#[view]
public fun balance_of(owner: address): u64 acquires TokenStore {
    // Only reads state
    borrow_global<TokenStore>(owner).balance
}
```

## Error Handling

```move
#[view]
public fun safe_balance_of(owner: address): u64 acquires TokenStore {
    // Handle non-existent resources gracefully
    if (!exists<TokenStore>(owner)) {
        return 0
    };

    let store = borrow_global<TokenStore>(owner);
    store.balance
}

#[view]
public fun get_allowance_or_max(
    owner: address,
    spender: address
): u64 acquires Allowances {
    if (!exists<Allowances>(owner)) {
        return 0xFFFFFFFFFFFFFFFF // Return max u64 if no allowance set
    };
    // Return actual allowance
    0
}
```

## Performance Considerations

View functions execute on fullnodes during API requests, so optimize for speed:

- Minimize the number of `borrow_global` calls
- Avoid nested loops with large iterations
- Use early returns to skip unnecessary computation
- Pre-compute complex values in transaction functions when possible
- Consider implementing summary resources for expensive aggregations

## Related Concepts

- [Module Structure](https://www.dwellir.com/docs/aptos/module_structure) - Organize view functions effectively
- [Resource Management](https://www.dwellir.com/docs/aptos/resource_management) - Read resources safely
- [Testing](https://www.dwellir.com/docs/aptos/testing) - Test view function logic
- [GraphQL API](https://www.dwellir.com/docs/aptos/graphql/overview) - Alternative querying approach

---

## Arbitrum - Leading Ethereum L2 Scaling Solution

# Arbitrum - Leading Ethereum L2 Scaling Solution

## Why Build on Arbitrum?

Arbitrum is the leading Ethereum Layer 2 scaling solution, processing more transactions than Ethereum mainnet itself. Built with Optimistic Rollup technology, Arbitrum offers:

### **Lightning Fast Performance**

- **Sub-second block times** - Ultra-fast L2 transaction confirmations
- **10-50x lower costs** than Ethereum mainnet
- **40,000 TPS capacity** - Massive throughput for any scale

### **Enterprise Security**

- **$15B+ secured** - Largest L2 by Total Value Locked
- **Ethereum security** - Full fraud proof protection
- **Battle-tested** - Processing 1M+ transactions daily since 2021

### **Massive Ecosystem**

- **3M+ unique addresses** - Largest L2 user base
- **600+ protocols** - Complete DeFi, Gaming, and NFT ecosystem
- **Native integrations** - GMX, Uniswap V3, Aave, Curve

## Quick Start with Arbitrum

Connect to Arbitrum One in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Arbitrum One mainnet
const provider = new JsonRpcProvider(
  'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Arbitrum One mainnet
const web3 = new Web3(
  'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Arbitrum:', chainId === 42161);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';
import { arbitrum } from 'viem/chains';

// Create Arbitrum client
const client = createPublicClient({
  chain: arbitrum,
  transport: http('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

## Network Information

| Parameter    | Value     | Details      |
| ------------ | --------- | ------------ |
| Chain ID     | 42161     | Mainnet      |
| Block Time   | 2 seconds | Average      |
| Gas Token    | ETH       | Native token |
| RPC Standard | Ethereum  | JSON-RPC 2.0 |

## API Reference

Arbitrum supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/). Access all standard methods plus L2-specific optimizations.

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // L2 specific: Check L1 data availability
  if (receipt.l1Fee) {
    console.log('L1 data cost:', receipt.l1Fee);
  }

  return receipt;
}
```

### Gas Optimization

Optimize gas costs on Arbitrum One:

```javascript
// Estimate L2 execution gas
const l2Gas = await provider.estimateGas(tx);

// Get current L1 data fee (Arbitrum specific)
const l1DataFee = await provider.send('eth_estimateL1Fee', [tx]);

// Total cost = L2 execution + L1 data posting
const totalCost = l2Gas + BigInt(l1DataFee);
```

### Event Filtering

Efficiently query contract events:

```javascript
// Query events with automatic retry and pagination
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 2000; // Arbitrum recommended batch size

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);
  }

  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine multiple RPC calls for optimal performance:

```javascript
const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] }
];

const results = await provider.send(batch);
```

### 2. **Connection Pooling**

Reuse provider instances to minimize connection overhead:

```javascript
// Singleton pattern for provider
class ArbitrumProvider {
  static instance = null;

  static getInstance() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
      );
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data to reduce API calls:

```javascript
const cache = new Map();

async function getCachedBlockData(blockNumber) {
  const key = `block_${blockNumber}`;

  if (!cache.has(key)) {
    const block = await provider.getBlock(blockNumber);
    cache.set(key, block);
  }

  return cache.get(key);
}
```

## Troubleshooting Common Issues

### Error: "Insufficient funds for L1 fee"

Arbitrum transactions require ETH for both L2 execution and L1 data availability:

```javascript
// Always account for L1 fees in balance checks
const balance = await provider.getBalance(address);
const l1Fee = await provider.send('eth_estimateL1Fee', [tx]);
const l2Gas = await provider.estimateGas(tx);
const totalRequired = l2Gas + BigInt(l1Fee) + tx.value;

if (balance < totalRequired) {
  throw new Error(`Need ${totalRequired - balance} more ETH`);
}
```

### Error: "Transaction underpriced"

Arbitrum uses EIP-1559 pricing. Always use dynamic gas pricing:

```javascript
// Get current fee data
const feeData = await provider.getFeeData();

const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: 21000n
};
```

### Error: "Rate limit exceeded"

Implement exponential backoff for resilient applications:

```javascript
async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 2 ** i * 1000));
      } else {
        throw error;
      }
    }
  }
}
```

## Migration Guide

### From Ethereum Mainnet

Moving from L1 to Arbitrum One requires minimal changes:

```javascript
// Before (Ethereum)
const provider = new JsonRpcProvider('https://eth-rpc.example.com');

// After (Arbitrum)
const provider = new JsonRpcProvider(
  'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Smart contracts work identically
// Same tooling and libraries
// Note: Different chain ID (42161)
// Note: Separate block numbers
// Note: L1 data fees apply
```

## Resources & Tools

### Official Resources

- [Arbitrum Documentation](https://docs.arbitrum.io)
- [Arbitrum Bridge](https://bridge.arbitrum.io)
- [Arbitrum Block Explorer](https://arbiscan.io)

### Developer Tools

- [Hardhat Config](https://docs.arbitrum.io/for-devs/quickstart-solidity-hardhat)
- [Foundry Setup](https://docs.arbitrum.io/for-devs/quickstart-solidity-hardhat)
- [Development Frameworks](https://docs.arbitrum.io/build-decentralized-apps/reference/development-frameworks)

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

***

*Start building on Arbitrum with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## arb_getL1ConfirmationNumber - Get L1 confir...

# arb_getL1ConfirmationNumber - Get L1 confir...

Get L1 confirmation number on the Arbitrum network.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "method": "arb_getL1ConfirmationNumber",
        "params": [],
        "id": 1
      }'
```

## Response Fields

- `result` (`OBJECT, required`): The return value depends on the specific method being called.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Implementation Example

cURL
JavaScript

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "arb_getL1ConfirmationNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'arb_getL1ConfirmationNumber',
    params: [],
    id: 1
  })
});

const data = await response.json();
console.log(data.result);
```

---

## debug_traceBlock - Arbitrum RPC Method

Traces all transactions in a block on Arbitrum by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Arbitrum - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Arbitrum
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Arbitrum RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Arbitrum:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Arbitrum blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/arbitrum/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/arbitrum/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Arbitrum RPC Method

Traces all transactions in a block on Arbitrum identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Arbitrum - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Arbitrum
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Arbitrum RPC Method
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Arbitrum:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Arbitrum:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/arbitrum/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/arbitrum/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/arbitrum/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Arbitrum RPC Method

Traces all transactions in a block on Arbitrum identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Arbitrum - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Arbitrum execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Arbitrum RPC Method
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Arbitrum:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Arbitrum:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/arbitrum/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/arbitrum/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/arbitrum/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Arbitrum RPC Method

Traces a call on Arbitrum without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Arbitrum - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Arbitrum
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Arbitrum RPC Method
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
        "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
        "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    data: '0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    data: '0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    'data': '0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
        "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Arbitrum to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Arbitrum:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Arbitrum:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/arbitrum/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/arbitrum/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Arbitrum RPC Method

Traces a transaction execution on Arbitrum by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Arbitrum
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2'

# debug_traceTransaction - Arbitrum RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/arbitrum/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/arbitrum/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Arbitrum RPC Method

Returns a list of addresses owned by the client on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/arbitrum/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Arbitrum RPC Method

Returns the number of the most recent block on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_blockNumber` is fundamental for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Syncing Applications** - Keep your dApp in sync with the latest Arbitrum blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Arbitrum block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Arbitrum block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Arbitrum block: {block_number}')

# eth_blockNumber - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Arbitrum block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Arbitrum block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Arbitrum:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Arbitrum:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Arbitrum node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check if node is still syncing

---

## eth_call - Arbitrum RPC Method

Executes a new message call immediately without creating a transaction on Arbitrum. Used for reading smart contract state.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_call` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Arbitrum
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';
const walletAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Arbitrum. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Arbitrum
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Arbitrum

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Arbitrum RPC Method
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "data": "0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    data := common.FromHex("0x70a0823100000000000000000000000013867a801e352e219c2d2AC29288Bf086e5C81ef")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Arbitrum RPC Method

Returns the chain ID used for transaction signing on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_chainId` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Arbitrum RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/arbitrum/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check sync status

---

## eth_coinbase - Arbitrum RPC Method

Checks the legacy `eth_coinbase` compatibility method on Arbitrum. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Arbitrum coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Arbitrum coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Arbitrum coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Arbitrum coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Arbitrum coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Arbitrum nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/arbitrum/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/arbitrum/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/arbitrum/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Arbitrum RPC Method

Estimates the gas necessary to execute a transaction on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Arbitrum
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0x13867a801e352e219c2d2AC29288Bf086e5C81ef', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0x13867a801e352e219c2d2AC29288Bf086e5C81ef', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Arbitrum.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Arbitrum may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "to": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Arbitrum RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0x13867a801e352e219c2d2AC29288Bf086e5C81ef', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/arbitrum/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Send transaction

---

## eth_estimateL1Fee - Estimate L1 data postin...

# eth_estimateL1Fee - Estimate L1 data postin...

Estimate L1 data posting fee on the Arbitrum network.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "method": "eth_estimateL1Fee",
        "params": [],
        "id": 1
      }'
```

## Response Fields

- `result` (`OBJECT, required`): The return value depends on the specific method being called.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Implementation Example

cURL
JavaScript

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateL1Fee",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_estimateL1Fee',
    params: [],
    id: 1
  })
});

const data = await response.json();
console.log(data.result);
```

---

## eth_feeHistory - Arbitrum RPC Method

Returns historical gas fee data on Arbitrum, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Arbitrum without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Arbitrum.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Arbitrum) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/arbitrum/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/arbitrum/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Arbitrum RPC Method

Returns the current gas price on Arbitrum in wei.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Arbitrum
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Arbitrum. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Arbitrum, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Arbitrum: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Arbitrum supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Arbitrum RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/arbitrum/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/arbitrum/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Arbitrum RPC Method

Returns the balance of a given address on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getBalance` is fundamental for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Arbitrum
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Arbitrum
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Arbitrum. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Arbitrum`);
  return balance;
}

displayBalance('0x13867a801e352e219c2d2AC29288Bf086e5C81ef');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Arbitrum at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Arbitrum')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Arbitrum')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Arbitrum.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Arbitrum RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/arbitrum/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Arbitrum RPC Method

Returns information about a block by hash on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getBlockByHash` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Arbitrum
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Arbitrum.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Arbitrum RPC Method
verify_block_still_canonical('0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Arbitrum.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Arbitrum RPC Method

Returns information about a block by block number on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getBlockByNumber` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Arbitrum
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Arbitrum with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Arbitrum. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Arbitrum RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Arbitrum RPC Method

# eth_getBlockReceipts - Arbitrum RPC Method

Returns all transaction receipts for a block on Arbitrum. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getBlockReceipts` is useful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Arbitrum, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Arbitrum. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xcb51ef4d410f0ddfdf102f18e40a7e44748001bae0b0f5bc065aadd13fa3c9b0",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Arbitrum RPC Method

Returns the bytecode at a given address on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getCode` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Arbitrum
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Arbitrum:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Arbitrum:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Arbitrum RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/arbitrum/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/arbitrum/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Arbitrum RPC Method

Polls a filter on Arbitrum and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getFilterChanges` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Arbitrum
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Arbitrum RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Arbitrum:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/arbitrum/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/arbitrum/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/arbitrum/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/arbitrum/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/arbitrum/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Arbitrum RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Arbitrum. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getFilterLogs` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Arbitrum
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Arbitrum RPC Method
FILTER_ID=$(curl -s -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/arbitrum/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/arbitrum/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/arbitrum/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Arbitrum RPC Method

# eth_getLogs - Arbitrum RPC Method

Returns an array of all logs matching a given filter object on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Arbitrum for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Arbitrum
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Arbitrum.

```javascript
const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Arbitrum
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Arbitrum

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/arbitrum/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Arbitrum RPC Method

Returns the value from a storage position at a given address on Arbitrum. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getStorageAt` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0x13867a801e352e219c2d2AC29288Bf086e5C81ef', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Arbitrum:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Arbitrum:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/arbitrum/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/arbitrum/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/arbitrum/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Arbitrum RPC Method

# eth_getTransactionByHash - Arbitrum RPC Method

Returns the information about a transaction by transaction hash on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getTransactionByHash` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Arbitrum
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Arbitrum. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Arbitrum. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Arbitrum RPC Method

Returns the number of transactions sent from an address on Arbitrum, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getTransactionCount` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Arbitrum
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0x13867a801e352e219c2d2AC29288Bf086e5C81ef', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Arbitrum nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0x13867a801e352e219c2d2AC29288Bf086e5C81ef'
nonce = get_transaction_count(address)
print(f'Arbitrum nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Arbitrum nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Arbitrum:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0x13867a801e352e219c2d2AC29288Bf086e5C81ef');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/arbitrum/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Arbitrum RPC Method

# eth_getTransactionReceipt - Arbitrum RPC Method

Returns the receipt of a transaction by transaction hash on Arbitrum. Receipt is only available for mined transactions.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_getTransactionReceipt` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Arbitrum and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Arbitrum. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Arbitrum, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x6d26bc1cde1ce724eadaf4448431aa79a323ab58212c4ec39b5569b201ffaee2")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/arbitrum/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Arbitrum RPC Method

Returns the legacy `eth_hashrate` compatibility value on Arbitrum. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Arbitrum hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Arbitrum hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Arbitrum hashrate: {hashrate} H/s')

# eth_hashrate - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Arbitrum hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Arbitrum hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/arbitrum/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/arbitrum/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Arbitrum RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Arbitrum. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Arbitrum
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Arbitrum priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Arbitrum priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Arbitrum priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Arbitrum:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Arbitrum:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/arbitrum/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/arbitrum/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Arbitrum RPC Method

Checks the legacy `eth_mining` compatibility method on Arbitrum. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Arbitrum mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Arbitrum mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Arbitrum mining: {mining["result"]}')

# eth_mining - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Arbitrum mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Arbitrum mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/arbitrum/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/arbitrum/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Arbitrum RPC Method

Creates a filter on Arbitrum that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_newBlockFilter` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Block Monitoring** - Detect new blocks on Arbitrum as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Arbitrum RPC Method
FILTER_ID=$(curl -s -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Arbitrum:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/arbitrum/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/arbitrum/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Arbitrum RPC Method

Creates a filter object on Arbitrum based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_newFilter` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Event Monitoring** - Subscribe to specific contract events on Arbitrum such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Arbitrum RPC Method
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0x13867a801e352e219c2d2AC29288Bf086e5C81ef",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Arbitrum:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0x13867a801e352e219c2d2AC29288Bf086e5C81ef',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/arbitrum/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/arbitrum/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/arbitrum/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/arbitrum/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Arbitrum RPC Method

Creates a filter on Arbitrum that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Mempool Monitoring** - Observe unconfirmed transactions on Arbitrum to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for high-volume DeFi (GMX, Uniswap, Aave), gaming, and cross-chain applications

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Arbitrum RPC Method
FILTER_ID=$(curl -s -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Arbitrum...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Arbitrum:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/arbitrum/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Arbitrum RPC Method

Returns the current Ethereum protocol version used by the Arbitrum node.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_protocolVersion` is useful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Arbitrum protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Arbitrum protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Arbitrum protocol version: {version}')

# eth_protocolVersion - Arbitrum RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Arbitrum protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Arbitrum protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Arbitrum nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/arbitrum/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/arbitrum/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Arbitrum RPC Method

Submits a pre-signed transaction for broadcast to Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Arbitrum
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0x13867a801e352e219c2d2AC29288Bf086e5C81ef', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Arbitrum mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/arbitrum/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Arbitrum RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0x13867a801e352e219c2d2AC29288Bf086e5C81ef")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/arbitrum/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/arbitrum/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Arbitrum RPC Method

Creates and sends a new transaction from an unlocked account on Arbitrum. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Arbitrum RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Arbitrum tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Arbitrum tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Arbitrum tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Arbitrum tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Arbitrum tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Arbitrum dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/arbitrum/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/arbitrum/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Arbitrum RPC Method

Signs a transaction with the private key of the specified account on Arbitrum without submitting it to the network.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Arbitrum RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Arbitrum tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Arbitrum tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Arbitrum tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Arbitrum:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/arbitrum/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/arbitrum/eth_accounts) - List accounts available for signing

---

## eth_syncing - Arbitrum RPC Method

# eth_syncing - Arbitrum RPC Method

Returns the sync status of your Arbitrum node - either `false` when fully synced, or an object describing the sync progress.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_syncing` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Arbitrum node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Arbitrum node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Arbitrum node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/arbitrum/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/arbitrum/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/arbitrum/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Arbitrum RPC Method

Removes a filter on Arbitrum that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`eth_uninstallFilter` is important for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Arbitrum RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/arbitrum/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/arbitrum/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/arbitrum/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/arbitrum/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/arbitrum/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Arbitrum RPC Method

Checks whether the connected Arbitrum client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`net_listening` is useful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Arbitrum node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Arbitrum node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Arbitrum RPC Method
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Arbitrum node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/arbitrum/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/arbitrum/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Get node client info

---

## net_peerCount - Arbitrum RPC Method

Returns the number of peers currently connected to your Arbitrum node.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`net_peerCount` is important for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Arbitrum deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Arbitrum peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Arbitrum peers: {peer_count}')

# net_peerCount - Arbitrum RPC Method
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Arbitrum peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Arbitrum nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/arbitrum/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/arbitrum/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Get node client info

---

## net_version - Arbitrum RPC Method

Returns the current network ID on Arbitrum as a decimal string. The network ID identifies which network the node is connected to.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`net_version` is essential for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Endpoint Identification** - Confirm your application is connected to the expected Arbitrum network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Arbitrum network ID:', networkId);

// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Arbitrum network ID: {network_id}')

# net_version - Arbitrum RPC Method
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Arbitrum network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/arbitrum/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/arbitrum/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/arbitrum/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check node sync progress

---

## rpc_modules - Arbitrum RPC Method

# rpc_modules - Arbitrum RPC Method

Returns the enabled JSON-RPC namespaces exposed by the connected Arbitrum endpoint together with their version strings.

> **Non-standard method.** `rpc_modules` is a client-introspection RPC that is commonly available on Geth-compatible stacks, but it is not part of the core Ethereum Execution API method set. Availability varies by client and operator policy.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`rpc_modules` is useful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Capability Discovery** - Detect whether namespaces like `debug`, `trace`, `txpool`, or `erigon` are exposed before attempting those calls
- **Client Diagnostics** - Verify what the serving node has enabled when debugging environment-specific issues
- **Infrastructure Audits** - Compare public and private endpoints to confirm which RPC surfaces are intentionally exposed
- **Runtime Feature Gating** - Adjust tooling behavior dynamically based on the actual namespaces available on a node

## Best Practices

- Call at startup to determine which features are available on a node
- Module availability varies by node client and provider configuration
- Use to gate feature access in applications before attempting unsupported calls
- This is a non-standard method; some endpoints may not expose it

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_modules",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Object, required`): Object whose keys are enabled namespaces and whose values are version strings

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "eth": "1.0",
    "net": "1.0",
    "web3": "1.0",
    "rpc": "1.0",
    "debug": "1.0",
    "trace": "1.0",
    "txpool": "1.0"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_modules",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const modules = await provider.send('rpc_modules', []);
console.log('Namespaces:', Object.keys(modules));

if (modules.debug) {
  console.log('Debug RPC is enabled');
}
```

```python
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'rpc_modules',
        'params': [],
        'id': 1,
    },
)

modules = response.json()['result']
print('Namespaces:', sorted(modules.keys()))
print('Has trace:', 'trace' in modules)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "sort"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var modules map[string]string
    err = client.CallContext(context.Background(), &modules, "rpc_modules")
    if err != nil {
        log.Fatal(err)
    }

    names := make([]string, 0, len(modules))
    for name := range modules {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Printf("Namespaces: %v\n", names)
}
```

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Inspect the client software version string
- [`debug_traceTransaction`](https://www.dwellir.com/docs/arbitrum/debug_traceTransaction) - Debug namespace example
- `trace_transaction` - Trace namespace example

---

## web3_clientVersion - Arbitrum RPC Method

Returns the current client software version string for your Arbitrum node, including the client name, version number, OS, and runtime.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

## When to Use This Method

`web3_clientVersion` is valuable for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Arbitrum client:', clientVersion);

// Using fetch
const response = await fetch('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Arbitrum client: {client_version}')

# web3_clientVersion - Arbitrum RPC Method
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Arbitrum client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/arbitrum/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/arbitrum/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/arbitrum/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/arbitrum/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Arbitrum RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Arbitrum.

> **Why Arbitrum?** Build on Ethereum's leading Layer 2 with 46% L2 market share and $12B+ TVL with full EVM compatibility, 1.5M daily transactions, and $3B+ DAO treasury for ecosystem growth.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for DeFi developers, protocol teams, and dApp builders seeking Ethereum scalability:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Arbitrum RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-arbitrum-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/arbitrum/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/arbitrum/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/arbitrum/web3_clientVersion) - Get node client version

---

## Asset Hub - Polkadot Asset Management

## Why Build on Asset Hub?

Asset Hub is Polkadot's native system parachain designed for efficient asset creation, management, and transfers. As a core infrastructure parachain, Asset Hub provides low-cost asset operations and seamless integration with the broader Polkadot ecosystem.

### **Native Asset Management**

- **Asset creation** - Mint fungible and non-fungible tokens natively
- **Low transaction costs** - Optimized for high-frequency asset operations
- **Cross-chain transfers** - Seamless XCM integration with all parachains
- **DOT integration** - Native support for Polkadot's main token

### **Multi-Network Support**

- **Polkadot Asset Hub** - Main network for production applications
- **Kusama Asset Hub** - Canary network for testing and experimentation
- **Westend Asset Hub** - Test network for development and staging
- **Paseo Asset Hub** - Community-operated testnet tracking Polkadot releases
- **Polkadot Sidecar** - Managed Sidecar API surface for REST-friendly data access

### **Performance & Efficiency**

- **Fast finality** - \~6 second block times with instant finality
- **Minimal fees** - Optimized for micro-transactions and asset operations
- **Substrate runtime** - Built on battle-tested Polkadot technology
- **XCM native** - First-class cross-chain messaging support

## Quick Start with Asset Hub

Connect to Asset Hub networks with Dwellir's reliable endpoints:

Dwellir serves Asset Hub across every public Polkadot environment:

- **Asset Hub Polkadot** – `wss://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY`
- **Asset Hub Kusama** – `wss://api-asset-hub-kusama.n.dwellir.com/YOUR_API_KEY`
- **Asset Hub Westend** – `https://api-asset-hub-westend.n.dwellir.com/YOUR_API_KEY`
- **Asset Hub Paseo** – `https://api-asset-hub-paseo.n.dwellir.com/YOUR_API_KEY`
- **Polkadot Sidecar** – `https://api-asset-hub-polkadot-sidecar.n.dwellir.com/YOUR_API_KEY`

The Sidecar tab exposes a managed Polkadot Sidecar deployment so you can issue REST requests (for example `/accounts` or `/blocks`) without running additional infrastructure.

### Installation & Setup

Direct JSON-RPC
Polkadot.js
Substrate API
Python (py-substrate-interface)

```bash
# Asset Hub - Polkadot Asset Management
curl -X POST https://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'

# Get latest finalized block
curl -X POST https://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'

# Query account balance
curl -X POST https://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9"],
    "id": 1
  }'
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

// Connect to Asset Hub Polkadot
const provider = new WsProvider('wss://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Verify connection
const chain = await api.rpc.system.chain();
const version = await api.rpc.system.version();
console.log(`Connected to ${chain} v${version}`);

// Get the latest block
const hash = await api.rpc.chain.getFinalizedHead();
const block = await api.rpc.chain.getBlock(hash);
console.log(`Latest block: #${block.block.header.number}`);

// Query account balance
const account = 'ACCOUNT_ADDRESS';
const balance = await api.query.system.account(account);
console.log(`Free balance: ${balance.data.free.toString()}`);
```

```typescript
import { createClient } from '@substrate/api';

// Connect to Asset Hub
const client = createClient({
  chainSpec: {
    genesisHash: '0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f',
    rpcUrls: ['https://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY']
  }
});

// Query chain information
const chainInfo = await client.getChainHead();
console.log('Current head:', chainInfo.hash);

// Get asset information
const assetId = 1000; // USDT on Asset Hub
const assetDetails = await client.query({
  type: 'assets',
  method: 'asset',
  args: [assetId]
});
```

```python
from substrateinterface import SubstrateInterface

# Connect to Asset Hub Polkadot
substrate = SubstrateInterface(
    url="wss://api-asset-hub-polkadot.n.dwellir.com/YOUR_API_KEY"
)

# Get chain information
chain_name = substrate.rpc_request("system_chain", [])
print(f"Connected to: {chain_name['result']}")

# Query account balance
account_info = substrate.query(
    module='System',
    storage_function='Account',
    params=['ACCOUNT_ADDRESS']
)

print(f"Free balance: {account_info['data']['free']}")

# Query asset details
asset_id = 1000  # USDT
asset_details = substrate.query(
    module='Assets',
    storage_function='Asset',
    params=[asset_id]
)

if asset_details:
    print(f"Asset name: {asset_details['name']}")
    print(f"Asset symbol: {asset_details['symbol']}")
    print(f"Decimals: {asset_details['decimals']}")
```

## Network Information

| Parameter    | Value         | Details            |
| ------------ | ------------- | ------------------ |
| Genesis Hash | 0x68d56f15... | Polkadot Asset Hub |
| Block Time   | 6 seconds     | Instant finality   |
| Native Token | DOT           | Polkadot token     |
| Parachain ID | 1000          | System parachain   |

### Network Details

| Parameter                       | Value                                       | Details |
| ------------------------------- | ------------------------------------------- | ------- |
| Polkadot Asset Hub Parachain ID | 1000                                        |         |
| Kusama Asset Hub Parachain ID   | 1000                                        |         |
| Westend Asset Hub Parachain ID  | 1000                                        |         |
| Native Token                    | DOT (Polkadot), KSM (Kusama), WND (Westend) |         |
| Consensus                       | Nominated Proof of Stake (via Relay Chain)  |         |
| Finality                        | Instant (via GRANDPA finality gadget)       |         |

## Core Features

### **Asset Management**

Create and manage fungible assets with native support:

```typescript
// Create a new asset
const createAsset = async (api, signer, assetId, admin, minBalance) => {
  const tx = api.tx.assets.create(assetId, admin, minBalance);
  const hash = await tx.signAndSend(signer);
  return hash;
};

// Set asset metadata
const setMetadata = async (api, signer, assetId, name, symbol, decimals) => {
  const tx = api.tx.assets.setMetadata(assetId, name, symbol, decimals);
  const hash = await tx.signAndSend(signer);
  return hash;
};

// Mint assets
const mintAsset = async (api, signer, assetId, beneficiary, amount) => {
  const tx = api.tx.assets.mint(assetId, beneficiary, amount);
  const hash = await tx.signAndSend(signer);
  return hash;
};
```

### **NFT Support**

Work with non-fungible tokens using the Uniques pallet:

```typescript
// Create an NFT collection
const createCollection = async (api, signer, collectionId, admin) => {
  const tx = api.tx.uniques.create(collectionId, admin);
  const hash = await tx.signAndSend(signer);
  return hash;
};

// Mint an NFT
const mintNFT = async (api, signer, collectionId, itemId, owner) => {
  const tx = api.tx.uniques.mint(collectionId, itemId, owner);
  const hash = await tx.signAndSend(signer);
  return hash;
};

// Set NFT metadata
const setNFTMetadata = async (api, signer, collectionId, itemId, data) => {
  const tx = api.tx.uniques.setMetadata(collectionId, itemId, data, false);
  const hash = await tx.signAndSend(signer);
  return hash;
};
```

### **Cross-Chain Transfers (XCM)**

Send assets across the Polkadot ecosystem:

```typescript
// Transfer DOT to another parachain
const xcmTransfer = async (api, signer, dest, beneficiary, amount) => {
  const destination = {
    V3: {
      parents: 1,
      interior: {
        X1: {
          Parachain: dest // Destination parachain ID
        }
      }
    }
  };

  const account = {
    V3: {
      parents: 0,
      interior: {
        X1: {
          AccountId32: {
            network: null,
            id: beneficiary
          }
        }
      }
    }
  };

  const assets = {
    V3: [
      {
        id: {
          Concrete: {
            parents: 1,
            interior: 'Here'
          }
        },
        fun: {
          Fungible: amount
        }
      }
    ]
  };

  const tx = api.tx.polkadotXcm.limitedReserveTransferAssets(
    destination,
    account,
    assets,
    0,
    'Unlimited'
  );

  const hash = await tx.signAndSend(signer);
  return hash;
};
```

## Common Integration Patterns

### **Multi-Asset Wallet**

Build wallets supporting multiple Asset Hub tokens:

```typescript
class AssetHubWallet {
  constructor(api, address) {
    this.api = api;
    this.address = address;
  }

  async getAllBalances() {
    const balances = {};

    // Get DOT balance
    const account = await this.api.query.system.account(this.address);
    balances.DOT = {
      free: account.data.free.toString(),
      reserved: account.data.reserved.toString(),
      symbol: 'DOT',
      decimals: 10
    };

    // Get asset balances
    const assets = await this.api.query.assets.account.entries(this.address);

    for (const [key, balance] of assets) {
      const assetId = key.args[0].toString();
      const metadata = await this.api.query.assets.metadata(assetId);

      balances[metadata.symbol.toString()] = {
        free: balance.unwrap().balance.toString(),
        assetId,
        symbol: metadata.symbol.toString(),
        decimals: metadata.decimals.toNumber()
      };
    }

    return balances;
  }

  async transferAsset(assetId, recipient, amount) {
    let tx;

    if (assetId === 'DOT') {
      // Native DOT transfer
      tx = this.api.tx.balances.transferKeepAlive(recipient, amount);
    } else {
      // Asset transfer
      tx = this.api.tx.assets.transferKeepAlive(assetId, recipient, amount);
    }

    return tx;
  }
}
```

### **Asset Analytics**

Track asset metrics and usage:

```typescript
class AssetAnalytics {
  constructor(api) {
    this.api = api;
  }

  async getAssetInfo(assetId) {
    const [details, metadata] = await Promise.all([
      this.api.query.assets.asset(assetId),
      this.api.query.assets.metadata(assetId)
    ]);

    if (details.isNone) {
      throw new Error(`Asset ${assetId} not found`);
    }

    const assetDetails = details.unwrap();

    return {
      id: assetId,
      name: metadata.name.toString(),
      symbol: metadata.symbol.toString(),
      decimals: metadata.decimals.toNumber(),
      supply: assetDetails.supply.toString(),
      accounts: assetDetails.accounts.toNumber(),
      sufficients: assetDetails.sufficients.toNumber(),
      owner: assetDetails.owner.toString(),
      issuer: assetDetails.issuer.toString(),
      admin: assetDetails.admin.toString(),
      freezer: assetDetails.freezer.toString(),
      minBalance: assetDetails.minBalance.toString(),
      isSufficient: assetDetails.isSufficient.toBoolean()
    };
  }

  async getTopAssets(limit = 10) {
    const assets = [];

    // Get all assets
    const assetEntries = await this.api.query.assets.asset.entries();

    for (const [key, details] of assetEntries) {
      const assetId = key.args[0].toString();
      const metadata = await this.api.query.assets.metadata(assetId);

      assets.push({
        id: assetId,
        symbol: metadata.symbol.toString(),
        supply: details.unwrap().supply.toString(),
        accounts: details.unwrap().accounts.toNumber()
      });
    }

    // Sort by number of accounts (popularity)
    return assets
      .sort((a, b) => b.accounts - a.accounts)
      .slice(0, limit);
  }
}
```

### **NFT Marketplace Integration**

Integrate with NFT collections:

```typescript
class NFTCollection {
  constructor(api, collectionId) {
    this.api = api;
    this.collectionId = collectionId;
  }

  async getCollectionInfo() {
    const [details, metadata] = await Promise.all([
      this.api.query.uniques.class(this.collectionId),
      this.api.query.uniques.classMetadataOf(this.collectionId)
    ]);

    return {
      id: this.collectionId,
      owner: details.unwrap().owner.toString(),
      issuer: details.unwrap().issuer.toString(),
      admin: details.unwrap().admin.toString(),
      freezer: details.unwrap().freezer.toString(),
      totalDeposit: details.unwrap().totalDeposit.toString(),
      freeHolding: details.unwrap().freeHolding.toBoolean(),
      instances: details.unwrap().instances.toNumber(),
      instanceMetadatas: details.unwrap().instanceMetadatas.toNumber(),
      attributes: details.unwrap().attributes.toNumber(),
      isFrozen: details.unwrap().isFrozen.toBoolean(),
      metadata: metadata.isSome ? metadata.unwrap().data.toString() : null
    };
  }

  async getAllItems() {
    const items = [];
    const itemEntries = await this.api.query.uniques.asset.entries();

    for (const [key, details] of itemEntries) {
      const [collectionId, itemId] = key.args;

      if (collectionId.toString() === this.collectionId.toString()) {
        const metadata = await this.api.query.uniques.instanceMetadataOf(
          collectionId,
          itemId
        );

        items.push({
          collectionId: collectionId.toString(),
          itemId: itemId.toString(),
          owner: details.unwrap().owner.toString(),
          approved: details.unwrap().approved.isSome
            ? details.unwrap().approved.unwrap().toString()
            : null,
          isFrozen: details.unwrap().isFrozen.toBoolean(),
          deposit: details.unwrap().deposit.toString(),
          metadata: metadata.isSome ? metadata.unwrap().data.toString() : null
        });
      }
    }

    return items;
  }
}
```

## Performance Optimization

### 1. **Efficient State Queries**

Batch multiple queries for better performance:

```typescript
async function batchQueries(api, queries) {
  const results = await Promise.all(queries.map(query => {
    if (query.type === 'storage') {
      return api.query[query.module][query.method](https://www.dwellir.com/docs/...query.args);
    } else if (query.type === 'rpc') {
      return api.rpc[query.module][query.method](https://www.dwellir.com/docs/...query.args);
    }
  }));

  return results;
}

// Example usage
const queries = [
  { type: 'storage', module: 'system', method: 'account', args: [address] },
  { type: 'storage', module: 'assets', method: 'asset', args: [1000] },
  { type: 'rpc', module: 'chain', method: 'getFinalizedHead', args: [] }
];

const [account, asset, head] = await batchQueries(api, queries);
```

### 2. **Event Subscription**

Monitor blockchain events efficiently:

```typescript
async function subscribeToAssetEvents(api, callback) {
  const unsubscribe = await api.query.system.events((events) => {
    events.forEach((record) => {
      const { event } = record;

      if (api.events.assets.Transferred.is(event)) {
        const [assetId, from, to, amount] = event.data;
        callback({
          type: 'AssetTransferred',
          assetId: assetId.toString(),
          from: from.toString(),
          to: to.toString(),
          amount: amount.toString()
        });
      } else if (api.events.assets.Created.is(event)) {
        const [assetId, creator, owner] = event.data;
        callback({
          type: 'AssetCreated',
          assetId: assetId.toString(),
          creator: creator.toString(),
          owner: owner.toString()
        });
      }
    });
  });

  return unsubscribe;
}
```

## Developer Resources

### **Official Resources**

- [Polkadot Developer Docs](https://docs.polkadot.com/polkadot-protocol/architecture/system-chains/asset-hub/)
- [Asset Hub Wiki](https://wiki.polkadot.com/learn/learn-assets/)
- [Substrate Documentation](https://docs.substrate.io/)

### **Developer Tools**

- [Polkadot.js Apps](https://polkadot.js.org/apps/) - Web interface for Asset Hub
- [Polkadot.js API](https://polkadot.js.org/docs/) - JavaScript SDK
- [Subxt](https://github.com/paritytech/subxt) - Rust SDK for Substrate chains
- [py-substrate-interface](https://github.com/polkascan/py-substrate-interface) - Python SDK

### **Block Explorers**

- [Polkadot.js Apps Explorer](https://polkadot.js.org/apps/#/explorer)
- [Subscan Asset Hub](https://assethub-polkadot.subscan.io/)

### **Example Projects**

- [Asset Transfer Tools](https://github.com/paritytech/asset-transfer-api)
- [XCM Tools](https://docs.polkadot.com/develop/toolkit/interoperability/xcm-tools/)
- [Asset Hub Examples](https://github.com/bee344/asset-hub-examples)

### Need Help?

- **Email**: <support@dwellir.com>
- **Polkadot Wiki**: [wiki.polkadot.network](https://wiki.polkadot.network)
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)
- **Community**: [Polkadot Discord](https://discord.gg/polkadot)

***

*Build the future of cross-chain asset management on Asset Hub with Dwellir's reliable infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## author_pendingExtrinsics - Asset Hub RPC Method

Returns all pending extrinsics currently in the transaction pool on Asset Hub. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`author_pendingExtrinsics` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Asset Hub
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Asset Hub transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/asset-hub/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Asset Hub RPC Method

Generate a new set of session keys on Asset Hub. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`author_rotateKeys` is critical for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Validator Setup** - Generate initial session keys when setting up a new validator on native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Asset Hub RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Asset Hub:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/asset-hub/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Asset Hub RPC Method

Submits a signed extrinsic to Asset Hub and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Asset Hub
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Asset Hub RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://asset-hub-polkadot-rpc.n.dwellir.com

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://asset-hub-polkadot-rpc.n.dwellir.com');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://asset-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://asset-hub-polkadot-rpc.n.dwellir.com").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/asset-hub/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/asset-hub/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Asset Hub RPC Method

Submits a fully signed extrinsic to Asset Hub for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`author_submitExtrinsic` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Token Transfers** -- Send native tokens or assets between accounts on Asset Hub
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Asset Hub RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://asset-hub-polkadot-rpc.n.dwellir.com").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/asset-hub/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/asset-hub/payment_queryInfo) -- Estimate fees before submitting
- `system_accountNextIndex` -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Asset Hub RPC Method

# beefy_getFinalizedHead - Asset Hub RPC Method

Returns the block hash of the latest BEEFY-finalized block on Asset Hub. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`beefy_getFinalizedHead` is important for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Cross-Chain Bridges** - Verify finality proofs efficiently for bridge operations on native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Light Clients** - Verify finality without downloading full GRANDPA justifications
- **Trustless Bridges** - Generate compact finality proofs that can be verified on external chains
- **Bridge Monitoring** - Track BEEFY finality progress relative to GRANDPA finality

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest BEEFY-finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response (BEEFY Not Enabled)

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "BEEFY is not enabled on this chain"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

try {
  // Get BEEFY finalized head
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  console.log('BEEFY finalized:', beefyHead.toHex());

  // Compare with GRANDPA finalized
  const grandpaHead = await api.rpc.chain.getFinalizedHead();
  console.log('GRANDPA finalized:', grandpaHead.toHex());

  // Get block numbers for comparison
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const grandpaBlock = await api.rpc.chain.getBlock(grandpaHead);

  const beefyNum = beefyBlock.block.header.number.toNumber();
  const grandpaNum = grandpaBlock.block.header.number.toNumber();
  console.log(`BEEFY lag behind GRANDPA: ${grandpaNum - beefyNum} blocks`);
} catch (error) {
  console.error('BEEFY may not be enabled:', error.message);
}

await api.disconnect();
```

```python
import requests

def get_beefy_finalized_head():
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'beefy_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"BEEFY error: {result['error']['message']}")

    return result['result']

def get_grandpa_finalized_head():
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 2
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

try:
    beefy_hash = get_beefy_finalized_head()
    grandpa_hash = get_grandpa_finalized_head()
    print(f'BEEFY finalized: {beefy_hash}')
    print(f'GRANDPA finalized: {grandpa_hash}')
except Exception as e:
    print(f'Error: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Call beefy_getFinalizedHead via raw RPC
    let beefy_head: Value = api.rpc()
        .request("beefy_getFinalizedHead", subxt::rpc_params![])
        .await?;

    println!("BEEFY finalized: {}", beefy_head);

    // Compare with GRANDPA finalized
    let grandpa_head = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("GRANDPA finalized: {:?}", grandpa_head);

    Ok(())
}
```

## Common Use Cases

### 1. Bridge Finality Verification

Verify BEEFY finality before relaying messages on a cross-chain bridge:

```javascript
async function verifyBridgeFinality(api, targetBlockHash) {
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const beefyNumber = beefyBlock.block.header.number.toNumber();

  const targetBlock = await api.rpc.chain.getBlock(targetBlockHash);
  const targetNumber = targetBlock.block.header.number.toNumber();

  if (beefyNumber >= targetNumber) {
    console.log(`Block #${targetNumber} has BEEFY finality - safe to relay`);
    return true;
  } else {
    console.log(`Waiting: BEEFY at #${beefyNumber}, target at #${targetNumber}`);
    return false;
  }
}
```

### 2. BEEFY vs GRANDPA Finality Monitor

Track the gap between the two finality gadgets:

```javascript
async function monitorFinalityGadgets(api) {
  setInterval(async () => {
    try {
      const [beefyHead, grandpaHead] = await Promise.all([
        api.rpc.beefy.getFinalizedHead(),
        api.rpc.chain.getFinalizedHead()
      ]);

      const [beefyBlock, grandpaBlock] = await Promise.all([
        api.rpc.chain.getBlock(beefyHead),
        api.rpc.chain.getBlock(grandpaHead)
      ]);

      const beefyNum = beefyBlock.block.header.number.toNumber();
      const grandpaNum = grandpaBlock.block.header.number.toNumber();
      const lag = grandpaNum - beefyNum;

      console.log(`GRANDPA: #${grandpaNum} | BEEFY: #${beefyNum} | Lag: ${lag} blocks`);
    } catch (error) {
      console.error('Monitor error:', error.message);
    }
  }, 12000);
}
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/asset-hub/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Asset Hub RPC Method

Retrieves complete block information from Asset Hub, including the block header, extrinsics, and justifications.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Asset Hub RPC Method
curl https://asset-hub-polkadot-rpc.n.dwellir.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://asset-hub-polkadot-rpc.n.dwellir.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/asset-hub/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/asset-hub/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Asset Hub RPC Method

Returns the block hash for a given block number on Asset Hub. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`chain_getBlockHash` is fundamental for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Asset Hub
- **Block Navigation** - Navigate the blockchain history for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Asset Hub
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Asset Hub for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Asset Hub state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Asset Hub network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/asset-hub/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Asset Hub RPC Method

Returns the hash of the last finalized block on Asset Hub. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`chain_getFinalizedHead` is critical for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Asset Hub
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Asset Hub RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://asset-hub-polkadot-rpc.n.dwellir.com', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Asset Hub:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/asset-hub/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/asset-hub/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHeader - Asset Hub RPC Method

Returns the block header for a given hash on Asset Hub. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`chain_getHeader` is ideal for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Asset Hub
- **Chain Synchronization** - Track block production and monitor chain progress for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Asset Hub:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Asset Hub chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Asset Hub block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/asset-hub/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_subscribeFinalizedHeads - Asset Hub RPC Method

Subscribe to receive notifications when blocks are finalized on Asset Hub. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Exchange Deposits** - Only credit funds after finalization for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://asset-hub-polkadot-rpc.n.dwellir.com`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Asset Hub RPC Method
wscat -c wss://asset-hub-polkadot-rpc.n.dwellir.com -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://asset-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Asset Hub:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/asset-hub/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Asset Hub RPC Method

Subscribe to receive notifications when new block headers are produced on Asset Hub. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`chain_subscribeNewHeads` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Block Monitoring** - Track new blocks in real time on Asset Hub for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://asset-hub-polkadot-rpc.n.dwellir.com`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Asset Hub RPC Method
wscat -c wss://asset-hub-polkadot-rpc.n.dwellir.com -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://asset-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Asset Hub as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Asset Hub:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/asset-hub/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/asset-hub/chain_getBlock) - Get full block data with extrinsics
- `chain_unsubscribeNewHeads` - Unsubscribe from new heads

---

## grandpa_roundState - Asset Hub RPC Method

Returns the state of the current GRANDPA finality round on Asset Hub when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`grandpa_roundState` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Finality Monitoring** -- Track whether GRANDPA rounds are progressing normally or stalling on Asset Hub, critical for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Consensus Health Checks** -- Detect finality delays by comparing prevote/precommit counts against the supermajority threshold weight
- **Validator Participation Analysis** -- Monitor which validators are actively voting and whether the authority set has sufficient online weight
- **Authority Set Tracking** -- Observe `setId` changes after validator set rotations to verify smooth authority transitions
- **Capability Detection** -- Confirm whether the shared endpoint exposes GRANDPA round internals before you build monitoring around them

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

- `setId` (`u64, required`): The current GRANDPA authority set ID; increments when the validator set changes
- `best` (`RoundState, required`): State of the best (most recent) active round
- `background` (`Vec<RoundState>, required`): Background rounds that are still being tracked (typically the previous round)
- `round` (`u64, required`): The round number
- `totalWeight` (`u64, required`): Total combined weight of all authorities in this set
- `thresholdWeight` (`u64, required`): Minimum weight required for a supermajority (2/3 + 1 of totalWeight)
- `prevotes` (`Votes, required`): Current prevote state for this round
- `precommits` (`Votes, required`): Current precommit state for this round
- `currentWeight` (`u64, required`): Total weight of votes received so far
- `missing` (`Vec<AuthorityId>, required`): List of authority public keys that have not yet voted

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "setId": 4821,
    "best": {
      "round": 19384,
      "totalWeight": 297,
      "thresholdWeight": 199,
      "prevotes": {
        "currentWeight": 297,
        "missing": []
      },
      "precommits": {
        "currentWeight": 264,
        "missing": [
          "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
          "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
        ]
      }
    },
    "background": []
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

try {
  const roundState = await api.rpc.grandpa.roundState();
  const best = roundState.best;

  console.log('Authority set ID:', roundState.setId.toString());
  console.log('Round:', best.round.toString());
  console.log('Total weight:', best.totalWeight.toString());
  console.log('Threshold weight:', best.thresholdWeight.toString());
  console.log('Prevote weight:', best.prevotes.currentWeight.toString());
  console.log('Precommit weight:', best.precommits.currentWeight.toString());
  console.log('Missing precommits:', best.precommits.missing.length);
} catch (error) {
  console.log('grandpa_roundState unsupported:', error.message);
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('grandpa_roundState unsupported:', payload.error.message);
} else {
  console.log('Set ID:', payload.result.setId);
  console.log('Best round:', payload.result.best.round);
  console.log('Prevote progress:', payload.result.best.prevotes.currentWeight, '/', payload.result.best.thresholdWeight);
  console.log('Precommit progress:', payload.result.best.precommits.currentWeight, '/', payload.result.best.thresholdWeight);
}
```

```python
import requests

def get_grandpa_round_state():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'grandpa_roundState',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

try:
    state = get_grandpa_round_state()
    best = state['best']

    print(f"Authority set ID: {state['setId']}")
    print(f"Round: {best['round']}")
    print(f"Prevote: {best['prevotes']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Precommit: {best['precommits']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Missing precommit voters: {len(best['precommits']['missing'])}")
except KeyError:
    print('grandpa_roundState unsupported on this endpoint')

# grandpa_roundState - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
response = substrate.rpc_request('grandpa_roundState', [])
if 'error' in response:
    print(f"grandpa_roundState unsupported: {response['error']['message']}")
else:
    print(f"Set ID: {response['result']['setId']}, Round: {response['result']['best']['round']}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
    set_id: u64,
    best: BestRound,
    background: Vec<BestRound>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct BestRound {
    round: u64,
    total_weight: u64,
    threshold_weight: u64,
    prevotes: Votes,
    precommits: Votes,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Votes {
    current_weight: u64,
    missing: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "grandpa_roundState",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    if body.get("error").is_some() {
        println!("grandpa_roundState unsupported: {}", body["error"]["message"]);
        return Ok(());
    }

    let state: RoundState = serde_json::from_value(body["result"].clone())?;

    println!("Set ID: {}", state.set_id);
    println!("Round: {}", state.best.round);
    println!("Prevote: {}/{}", state.best.prevotes.current_weight, state.best.threshold_weight);
    println!("Precommit: {}/{}", state.best.precommits.current_weight, state.best.threshold_weight);
    println!("Missing precommit voters: {}", state.best.precommits.missing.len());

    Ok(())
}
```

## Common Use Cases

### 1. Finality Health Monitoring

Periodically check whether GRANDPA rounds are progressing and alert on stalls:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorFinality(api, intervalMs = 10000) {
  let lastRound = 0;
  let lastSetId = 0;
  let stallCount = 0;

  setInterval(async () => {
    const state = await api.rpc.grandpa.roundState();
    const best = state.best;
    const round = best.round.toNumber();
    const setId = state.setId.toNumber();
    const prevoteProgress = best.prevotes.currentWeight.toNumber();
    const precommitProgress = best.precommits.currentWeight.toNumber();
    const threshold = best.thresholdWeight.toNumber();

    if (setId !== lastSetId) {
      console.log(`Authority set changed: ${lastSetId} -> ${setId}`);
      lastSetId = setId;
    }

    if (round === lastRound) {
      stallCount++;
      if (stallCount >= 3) {
        console.warn(`GRANDPA round ${round} stalled for ${stallCount} checks`);
        console.warn(`  Prevotes: ${prevoteProgress}/${threshold}`);
        console.warn(`  Precommits: ${precommitProgress}/${threshold}`);
        console.warn(`  Missing voters: ${best.precommits.missing.length}`);
      }
    } else {
      stallCount = 0;
      console.log(`Round ${round} | prevotes=${prevoteProgress}/${threshold} precommits=${precommitProgress}/${threshold}`);
    }

    lastRound = round;
  }, intervalMs);
}
```

### 2. Validator Participation Report

Generate a report of which validators are consistently missing votes:

```javascript
async function trackMissingVoters(api, samples = 20, delayMs = 6000) {
  const missingCounts = {};

  for (let i = 0; i < samples; i++) {
    const state = await api.rpc.grandpa.roundState();
    const missing = state.best.precommits.missing;

    missing.forEach((authority) => {
      const key = authority.toString();
      missingCounts[key] = (missingCounts[key] || 0) + 1;
    });

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  // Sort by most frequently missing
  const sorted = Object.entries(missingCounts)
    .sort(([, a], [, b]) => b - a);

  console.log('Validator participation report:');
  sorted.forEach(([authority, count]) => {
    const missRate = ((count / samples) * 100).toFixed(1);
    console.log(`  ${authority}: missed ${count}/${samples} (${missRate}%)`);
  });

  return sorted;
}
```

### 3. Supported-Fallback Check

If the endpoint does not expose GRANDPA round internals, fall back to finalized-head tracking:

```javascript
async function getFinalitySignal(api) {
  try {
    return { supported: true, roundState: await api.rpc.grandpa.roundState() };
  } catch (error) {
    return {
      supported: false,
      finalizedHead: (await api.rpc.chain.getFinalizedHead()).toHex(),
      message: error.message
    };
  }
}
```

### 3. Finality Lag Detection

Compare the finalized head with the best block to measure finality lag:

```javascript
async function getFinalityLag(api) {
  const [roundState, finalizedHash, bestHeader] = await Promise.all([
    api.rpc.grandpa.roundState(),
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader()
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);
  const bestNumber = bestHeader.number.toNumber();
  const finalizedNumber = finalizedHeader.number.toNumber();
  const lag = bestNumber - finalizedNumber;

  return {
    bestBlock: bestNumber,
    finalizedBlock: finalizedNumber,
    lagBlocks: lag,
    grandpaRound: roundState.best.round.toNumber(),
    setId: roundState.setId.toNumber(),
    prevoteReached: roundState.best.prevotes.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber(),
    precommitReached: roundState.best.precommits.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber()
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/asset-hub/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/asset-hub/system_health) -- Check overall node health including sync and peer status

---

## payment_queryFeeDetails - Asset Hub RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Asset Hub. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`payment_queryFeeDetails` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Asset Hub
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/asset-hub/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/asset-hub/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Asset Hub RPC Method

Estimates the fee for an encoded extrinsic on Asset Hub. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`payment_queryInfo` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Asset Hub
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Asset Hub RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/asset-hub/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/asset-hub/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/asset-hub/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/asset-hub/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Asset Hub RPC Method

Returns a list of all RPC methods exposed by the Asset Hub node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`rpc_methods` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Asset Hub node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Asset Hub endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://asset-hub-polkadot-rpc.n.dwellir.com', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Asset Hub endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/asset-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Asset Hub RPC Method

Calls a runtime API function on Asset Hub and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_call` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/asset-hub/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/asset-hub/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) -- Get the node version for compatibility checking

---

## state_getKeys - JSON-RPC Method

# state_getKeys - JSON-RPC Method

## Description

Returns storage keys that match a given prefix. This JSON-RPC method is useful for discovering all storage entries under a specific module or querying multiple related storage items. Be cautious with broad prefixes as they may return large result sets.

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage key prefix to match
- `blockHash` (`string, optional`): Block hash to query at. If omitted, uses the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "<prefix>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of hex-encoded storage keys matching the prefix

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"
  ],
  "id": 1
}
```

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9"
  ],
  "id": 1
}
```

## Code Examples

Python
JavaScript
TypeScript (@polkadot/api)

```python
import requests
import json
from substrateinterface import SubstrateInterface

def get_storage_keys(prefix, block_hash=None):
    url = "https://api-asset-hub-polkadot.n.dwellir.com"
    headers = {
        "Content-Type": "application/json"
    }
    
    params = [prefix, block_hash] if block_hash else [prefix]
    
    payload = {
        "jsonrpc": "2.0",
        "method": "state_getKeys",
        "params": params,
        "id": 1
    }
    
    response = requests.post(url, headers=headers, data=json.dumps(payload))
    return response.json()["result"]

# Example: Get all validator preferences keys
def get_validator_keys():
    # Staking.Validators storage prefix
    prefix = "0x5f3e4907f716ac89b6347d15ececedca9320c2dc4f5d7af5b320b04e2d1a3ff3"
    keys = get_storage_keys(prefix)
    
    print(f"Found {len(keys)} validator preference entries")
    
    for key in keys:
        # Extract validator account from key
        validator_account = key[-64:]
        print(f"Validator: 0x{validator_account}")
    
    return keys

# Example: Query all keys under a module
def get_module_keys(module_prefix):
    keys = get_storage_keys(module_prefix)
    
    # Group keys by storage item
    storage_items = {}
    for key in keys:
        # Storage keys typically have a fixed prefix per item
        item_prefix = key[:66]  # First 33 bytes (66 hex chars)
        if item_prefix not in storage_items:
            storage_items[item_prefix] = []
        storage_items[item_prefix].append(key)
    
    return storage_items
```

```javascript
const getStorageKeys = async (prefix, blockHash = null) => {
  const params = blockHash ? [prefix, blockHash] : [prefix];
  
  const response = await fetch('https://api-asset-hub-polkadot.n.dwellir.com', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'state_getKeys',
      params: params,
      id: 1
    })
  });
  
  const data = await response.json();
  return data.result;
};

// Get all account keys (System.Account storage)
const accountPrefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9';
const accountKeys = await getStorageKeys(accountPrefix);
console.log(`Found ${accountKeys.length} accounts`);

// Extract account addresses from keys
accountKeys.forEach(key => {
  // The account address is the last 32 bytes of the key
  const addressHex = key.slice(-64);
  console.log('Account key:', key);
  console.log('Address portion:', addressHex);
});
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function queryStorageKeys() {
  const provider = new WsProvider('wss://api-asset-hub-polkadot.n.dwellir.com');
  const api = await ApiPromise.create({ provider });
  
  // Method 1: Using high-level API to get keys
  const accountKeys = await api.query.system.account.keys();
  console.log('Account addresses:', accountKeys.map(k => k.toHuman()));
  
  // Method 2: Using low-level RPC for custom prefixes
  const prefix = api.query.system.account.keyPrefix();
  const keys = await api.rpc.state.getKeys(prefix);
  console.log(`Found ${keys.length} account storage keys`);
  
  // Method 3: Get keys for a specific map entry
  const validatorKeys = await api.query.staking.validators.keys();
  console.log('Active validators:', validatorKeys.length);
  
  // Process keys to extract data
  for (const key of keys) {
    // Decode the storage key
    const keyHex = key.toHex();
    console.log('Storage key:', keyHex);
    
    // Get the value for this key
    const value = await api.rpc.state.getStorage(key);
    console.log('Storage value:', value.toHex());
  }
  
  await api.disconnect();
}

// Advanced: Query keys with pagination
async function getKeysPagedExample() {
  const api = await ApiPromise.create({ 
    provider: new WsProvider('wss://api-asset-hub-polkadot.n.dwellir.com') 
  });
  
  const prefix = api.query.system.account.keyPrefix();
  const pageSize = 100;
  let startKey = prefix;
  let allKeys = [];
  
  while (true) {
    // Note: state_getKeysPaged is used for pagination
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    
    if (keys.length === 0) break;
    
    allKeys = allKeys.concat(keys);
    startKey = keys[keys.length - 1];
    
    console.log(`Fetched ${keys.length} keys, total: ${allKeys.length}`);
    
    if (keys.length < pageSize) break;
  }
  
  console.log(`Total keys found: ${allKeys.length}`);
  await api.disconnect();
}
```

## Common Storage Prefixes

| Module   | Storage Item  | Prefix (example)                                                     |
| -------- | ------------- | -------------------------------------------------------------------- |
| System   | Account       | `0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9` |
| Balances | TotalIssuance | `0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80` |
| Staking  | Validators    | `0x5f3e4907f716ac89b6347d15ececedca9320c2dc4f5d7af5b320b04e2d1a3ff3` |
| Session  | NextKeys      | `0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609` |

## Batch Query Example

```javascript
// Efficiently query multiple storage values
async function batchQueryStorage(api, keys) {
  // Get all values in a single call
  const values = await api.rpc.state.queryStorageAt(keys);
  
  const results = {};
  keys.forEach((key, index) => {
    results[key.toString()] = values[index];
  });
  
  return results;
}

// Example usage
const keys = await getStorageKeys(accountPrefix);
const values = await batchQueryStorage(api, keys.slice(0, 10));
console.log('Batch query results:', values);
```

## Use Cases

1. **Account Discovery**: Find all accounts with balances
2. **Validator Enumeration**: List all validators in the network
3. **Storage Analysis**: Analyze storage usage by module
4. **Migration Scripts**: Iterate over storage for upgrades
5. **Indexing**: Build indexes of on-chain data

## Notes

- Large prefixes may return many keys - use pagination when available
- Keys are returned in lexicographical order
- The prefix must be hex-encoded
- Consider using `state_getKeysPaged` for large datasets
- Storage keys include both the storage prefix and the key data

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/asset-hub/state_getKeysPaged) - Get keys with pagination
- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) - Get storage value
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) - Get metadata to decode keys

---

## state_getKeysPaged - Asset Hub RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Asset Hub. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_getKeysPaged` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Asset Hub
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://asset-hub-polkadot-rpc.n.dwellir.com";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/asset-hub/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getMetadata - Asset Hub RPC Method

Returns the runtime metadata for Asset Hub as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_getMetadata` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Asset Hub
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Asset Hub RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Asset Hub:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Asset Hub:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/asset-hub/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) - Call runtime APIs described in metadata

---

## state_getRuntimeVersion - Asset Hub RPC Method

# state_getRuntimeVersion - Asset Hub RPC Method

Returns the runtime version information for Asset Hub, including the spec name, spec version, implementation version, and supported API versions.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_getRuntimeVersion` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Asset Hub
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Asset Hub in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/asset-hub/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Asset Hub RPC Method

Returns the SCALE-encoded storage value for a given key on Asset Hub. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_getStorage` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Asset Hub
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Asset Hub RPC Method
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/asset-hub/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/asset-hub/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call runtime APIs for computed state that is not directly in storage
- `state_subscribeStorage` -- Subscribe to storage changes in real time via WebSocket

---

## state_queryStorageAt - Asset Hub RPC Method

Queries multiple storage keys at a specific block on Asset Hub, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_queryStorageAt` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Asset Hub
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Asset Hub RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/asset-hub/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/asset-hub/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## system_chain - Asset Hub RPC Method

Returns the chain name of the Asset Hub network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`system_chain` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Network Verification** -- Confirm your application is connected to the correct Asset Hub network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Asset Hub"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://asset-hub-polkadot-rpc.n.dwellir.com', 'Asset Hub');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/asset-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/asset-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/asset-hub/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/asset-hub/rpc_methods) -- List all available RPC methods

---

## system_health - Asset Hub RPC Method

# system_health - Asset Hub RPC Method

Returns the health status of the Asset Hub node, including peer count, sync state, and whether the node expects to have peers.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`system_health` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Asset Hub
- **Load Balancing** - Route requests only to healthy, fully synced nodes for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Asset Hub:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Asset Hub nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://asset-hub-polkadot-rpc.n.dwellir.com')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) - Get chain name
- `system_syncState` - Get detailed sync progress
- `system_peers` - Get detailed peer information

---

## system_name - Asset Hub RPC Method

Returns the node implementation name on Asset Hub. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`system_name` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Asset Hub
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Asset Hub node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Asset Hub node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Asset Hub node implementation: {name}')

# system_name - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
name = substrate.rpc_request('system_name', [])['result']
print(f'Asset Hub node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Asset Hub node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Asset Hub nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/asset-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/asset-hub/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/asset-hub/rpc_methods) -- List all available RPC methods

---

## system_properties - Asset Hub RPC Method

Returns the chain-specific properties for Asset Hub, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`system_properties` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Asset Hub
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/asset-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/asset-hub/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/asset-hub/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/asset-hub/rpc_methods) -- List all available RPC methods

---

## system_version - Asset Hub RPC Method

Returns the node implementation version string on Asset Hub. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`system_version` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Asset Hub
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Asset Hub node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://asset-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Asset Hub node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://asset-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Asset Hub node version: {version}')

# system_version - Asset Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://asset-hub-polkadot-rpc.n.dwellir.com')
version = substrate.rpc_request('system_version', [])['result']
print(f'Asset Hub node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://asset-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Asset Hub node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Asset Hub nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/asset-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/asset-hub/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/asset-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/asset-hub/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/asset-hub/rpc_methods) -- List all available RPC methods

---

## Astar RPC Guide

## Why Build on Astar?

### Multichain smart-contract hub

- Dual EVM + WASM runtimes let teams deploy Solidity and ink! contracts side by side while inheriting Polkadot relay-chain security ([Astar build docs](https://docs.astar.network/docs/build)).
- Build2Earn dApp staking shares block rewards with developers who attract stakers, turning usage into sustainable funding ([Astar dApp staking guide](https://docs.astar.network/docs/learn/dapp-staking/)).

### Optimized for Polkadot 2.0

- Astar adopted Agile Coretime so parachain capacity scales with demand instead of multi-year lease slots, reducing operating costs for long-lived apps ([Polkadot Ecosystem update, Aug 13 2025](https://polkadotecosystem.com/pt/dapps/smart-contracts/astar-network/)).
- Asynchronous Backing on mainnet brings \~6-second block production with larger block weight limits for latency-sensitive DeFi and gaming flows ([Astar mainnet upgrade announcement](https://astar.network/blog/astar-network-integrates-the-new-polkadot-generic-ledger-app-53)).

### ‍ Enterprise-ready infrastructure

- Dwellir, Chainstack, and other managed providers expose archive-grade RPC endpoints so teams can launch without running collators on day one ([Dwellir Astar network page](https://www.dwellir.com/networks/astar), [Chainstack support announcement](https://chainstack.com/chainstack-introduces-support-for-astar/)).

## Quick Start with Astar

Connect to Astar, Shibuya testnet, or Shiden canary through Dwellir-managed endpoints.

Sign up with [Dwellir](https://dashboard.dwellir.com/register) or your preferred infrastructure partner to replace `YOUR_API_KEY` before sending requests.

### Installation & Setup

cURL
JavaScript (polkadot.js)
Rust (subxt)
Python (py-substrate-interface)

```bash
curl https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "chain_getHeader",
    "params": []
  }'
```

**Sample response (2025-10-03 10:13 UTC):**

```json
{
  "jsonrpc": "2.0",
  "result": {
    "number": "0x9fbc28",
    "parentHash": "0x3f484429ad05365b32f643a9e825794961c1e6f92cde48a2c73935d7773dd5e1",
    "stateRoot": "0x1b983c37356b175bcf38dc1afcc8e31fa893e561f605fcad473697a68ac92169",
    "extrinsicsRoot": "0x783e75691edc604a3f13e1b5c558dd3ca5870ac07ce799c781713219a4a56224",
    "digest": {
      "logs": [
        "0x066175726120159b7a1100000000",
        "0x0452505352902bb51614ab3779fc4f4892aa8e21582a05a3162aef145209312f48c3b37fc7909618af06",
        "0x0466726f6e88015cd52a32691b3d44a3cef762382d728668dbee8c4509c697efe25708d5e6ffd400",
        "0x05617572610101809dd98345fce7c411d130f73a50b2e37d73efe027964dd63ce064275257cc1afb3192c7885ec1813c89350463bce9cd8b4f183b6e8cd779643ff4c476795e83"
      ]
    }
  },
  "id": 1
}
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function main() {
  const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
  const api = await ApiPromise.create({ provider });

  const [chain, nodeName, nodeVersion, runtime] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.state.getRuntimeVersion()
  ]);

  console.log(`Connected to ${chain.toString()} via ${nodeName} ${nodeVersion}`);
  console.log(`specVersion=${runtime.specVersion.toString()}, transactionVersion=${runtime.transactionVersion}`);

  const header = await api.rpc.chain.getHeader();
  console.log(`Latest block #${header.number} (${header.hash.toHex()})`);

  await api.disconnect();
}

main().catch(console.error);
```

```rust
use subxt::{config::substrate::SubstrateConfig, OnlineClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<SubstrateConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let block_hash = api.rpc().finalized_head().await?;
    let header = api.rpc().header(Some(block_hash)).await?.expect("header");

    println!("Finalized block #{:?}", header.number);
    println!("State root {:?}", header.state_root);
    Ok(())
}
```

```python
from substrateinterface import SubstrateInterface

api = SubstrateInterface(
    url="wss://api-astar.n.dwellir.com/YOUR_API_KEY",
    ss58_format=5,
    type_registry_preset="substrate-node-template"
)

runtime = api.get_runtime_version()
print("specVersion", runtime['specVersion'], "transactionVersion", runtime['transactionVersion'])

account = api.query(
    module='System',
    storage_function='Account',
    params=['ZEyDXf6563rc78ibEtYQAkHTSKLzMES1m2BrPNdLcma57Tg']
)
print("Free balance", account.value['data']['free'])
```

## Network Information

| Parameter            | Astar Mainnet                                                        | Shiden Canary      | Shibuya Testnet       |
| -------------------- | -------------------------------------------------------------------- | ------------------ | --------------------- |
| Relay chain          | Polkadot                                                             | Kusama             | Tokyo (Astar-managed) |
| Parachain ID         | 2006                                                                 | 2007               | 1000                  |
| Genesis hash         | `0x9eb76c5184c4ab8679d2d5d819fdf90b9c001403e9e17da2e14b6d8aec4029c6` |                    |                       |
| Runtime (2025-10-03) | specVersion `1700`, transactionVersion `3`                           | specVersion `1700` | specVersion `1700`    |
| Unit symbol          | ASTR                                                                 | SDN                | SBY                   |
| Decimals             | 18                                                                   | 18                 | 18                    |
| SS58 prefix          | 5                                                                    | 5                  | 5                     |
| Explorer             | astar.subscan.io                                                     | shiden.subscan.io  | shibuya.subscan.io    |

### Notes

| Parameter                 | Value                                                                                                                           | Details                                                                   |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Genesis Hash Verification | Recorded via `chain_getBlockHash(0)` on 2025-10-03                                                                              |                                                                           |
| Canary & Testnet Role     | Shiden (Kusama canary) and Shibuya (public testnet) provide staging environments for runtime upgrades before they reach mainnet | [Astar network overview](https://docs.astar.network/docs/learn/networks/) |

## API Reference

Astar exposes the full Substrate RPC surface for block data, storage, extrinsics, and fee estimation, with Frontier also enabling Ethereum-compatible `eth_*`, `net_*`, and `web3_*` endpoints for EVM tooling.

## Common Integration Patterns

### Subscribe to new heads for live indexing

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`Astar block #${header.number.toString()} => ${header.hash.toHex()}`);
});
```

### Fetch native balances via raw storage key

```bash
# Astar RPC Guide
curl https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e3a5c81a65b0e6ba8e72e48e58704482922ee694ea772dc63a575845fa57a0c5ea93dfdc2a93ec631d9e426962f1e311"
    ],
    "id": 42
  }'
```

Decode the SCALE-encoded result with polkadot.js or `py-substrate-interface` to access `data.free`, `data.reserved`, and staking locks.

### Pre-flight fees before submitting extrinsics

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

const api = await ApiPromise.create({ provider: new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY') });
const tx = api.tx.balances.transferKeepAlive(
  '5DAAnrj7VHTz5xy1mJQp7vR8J33ePZ8WqWnVfHfV9bPp7PPV',
  1_000_000_000_000
);
const info = await api.rpc.payment.queryInfo(tx.toHex());
console.log(info.toHuman());
```

## Performance Best Practices

- Prefer WebSocket connections for subscriptions and batched queries; fall back to HTTPS for stateless workloads.
- Cache runtime metadata keyed by the latest `specVersion` to avoid re-fetching on every request.
- Use `state_getKeysPaged` or `state_getStoragePaged` with bounded page sizes when crawling large maps such as staking ledgers.
- Track upgrade announcements on the [Astar forum](https://forum.astar.network/) and rehearse changes on Shiden before promoting them to mainnet.
- Implement exponential backoff around `author_submitExtrinsic`; Frontier queues may temporarily reject payloads when Ethereum traffic spikes.

## Troubleshooting

| Symptom                                 | Likely Cause                                  | Resolution                                                                                                             |
| --------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `API key does not exist` errors         | Misconfigured Dwellir endpoint or expired key | Re-issue the key via the [Dwellir dashboard](https://dashboard.dwellir.com/register) and update environment variables. |
| WebSocket disconnects on idle workloads | Provider closes idle connections              | Enable heartbeat pings or reconnect logic every 30 seconds.                                                            |

## Smoke Tests

Run these checks before promoting to production:

1. **system\_health:** Expect `isSyncing: false` and peer count > 8.
2. **chain\_getHeader:** Confirm block numbers advance every \~6 seconds.
3. **state\_getStorage(System.Account):** Verify SCALE decoding of balances for a known treasury or team account.
4. **payment\_queryInfo on a signed extrinsic:** Validate the fee model before broadcasting from CI/CD.

## Resources & Tools

- [Astar Documentation](https://docs.astar.network/): network architecture, node operations, and dApp staking guides.
- [Astar GitHub Releases](https://github.com/AstarNetwork/Astar/releases): runtime and client binaries.
- [Dwellir Astar Network Page](https://www.dwellir.com/networks/astar): managed endpoints and quick-start snippets.
- [Astar Subscan Explorer](https://astar.subscan.io): block, extrinsic, and account analytics for monitoring deployments.

Ready to deploy? Spin up a staging environment on Shibuya, soak test on Shiden, then cut over to Astar mainnet with confidence.

---

## author_pendingExtrinsics - Astar RPC Method

Returns all pending extrinsics currently in the transaction pool on Astar. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`author_pendingExtrinsics` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Astar
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Astar transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/astar/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/astar/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Astar RPC Method

Generate a new set of session keys on Astar. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`author_rotateKeys` is critical for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Validator Setup** - Generate initial session keys when setting up a new validator on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Astar RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Astar:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/astar/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/astar/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Astar RPC Method

Submits a signed extrinsic to Astar and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Astar
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Astar RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://api-astar.n.dwellir.com/YOUR_API_KEY

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://api-astar.n.dwellir.com/YOUR_API_KEY');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://api-astar.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://api-astar.n.dwellir.com/YOUR_API_KEY").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/astar/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/astar/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/astar/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Astar RPC Method

Submits a fully signed extrinsic to Astar for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`author_submitExtrinsic` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Token Transfers** -- Send native tokens or assets between accounts on Astar
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Astar RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://api-astar.n.dwellir.com/YOUR_API_KEY").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/astar/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/astar/payment_queryInfo) -- Estimate fees before submitting
- `system_accountNextIndex` -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Astar RPC Method

# beefy_getFinalizedHead - Astar RPC Method

Returns the block hash of the latest BEEFY-finalized block on Astar. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`beefy_getFinalizedHead` is important for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Cross-Chain Bridges** - Verify finality proofs efficiently for bridge operations on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Light Clients** - Verify finality without downloading full GRANDPA justifications
- **Trustless Bridges** - Generate compact finality proofs that can be verified on external chains
- **Bridge Monitoring** - Track BEEFY finality progress relative to GRANDPA finality

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest BEEFY-finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response (BEEFY Not Enabled)

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "BEEFY is not enabled on this chain"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  // Get BEEFY finalized head
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  console.log('BEEFY finalized:', beefyHead.toHex());

  // Compare with GRANDPA finalized
  const grandpaHead = await api.rpc.chain.getFinalizedHead();
  console.log('GRANDPA finalized:', grandpaHead.toHex());

  // Get block numbers for comparison
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const grandpaBlock = await api.rpc.chain.getBlock(grandpaHead);

  const beefyNum = beefyBlock.block.header.number.toNumber();
  const grandpaNum = grandpaBlock.block.header.number.toNumber();
  console.log(`BEEFY lag behind GRANDPA: ${grandpaNum - beefyNum} blocks`);
} catch (error) {
  console.error('BEEFY may not be enabled:', error.message);
}

await api.disconnect();
```

```python
import requests

def get_beefy_finalized_head():
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'beefy_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"BEEFY error: {result['error']['message']}")

    return result['result']

def get_grandpa_finalized_head():
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 2
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

try:
    beefy_hash = get_beefy_finalized_head()
    grandpa_hash = get_grandpa_finalized_head()
    print(f'BEEFY finalized: {beefy_hash}')
    print(f'GRANDPA finalized: {grandpa_hash}')
except Exception as e:
    print(f'Error: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Call beefy_getFinalizedHead via raw RPC
    let beefy_head: Value = api.rpc()
        .request("beefy_getFinalizedHead", subxt::rpc_params![])
        .await?;

    println!("BEEFY finalized: {}", beefy_head);

    // Compare with GRANDPA finalized
    let grandpa_head = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("GRANDPA finalized: {:?}", grandpa_head);

    Ok(())
}
```

## Common Use Cases

### 1. Bridge Finality Verification

Verify BEEFY finality before relaying messages on a cross-chain bridge:

```javascript
async function verifyBridgeFinality(api, targetBlockHash) {
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const beefyNumber = beefyBlock.block.header.number.toNumber();

  const targetBlock = await api.rpc.chain.getBlock(targetBlockHash);
  const targetNumber = targetBlock.block.header.number.toNumber();

  if (beefyNumber >= targetNumber) {
    console.log(`Block #${targetNumber} has BEEFY finality - safe to relay`);
    return true;
  } else {
    console.log(`Waiting: BEEFY at #${beefyNumber}, target at #${targetNumber}`);
    return false;
  }
}
```

### 2. BEEFY vs GRANDPA Finality Monitor

Track the gap between the two finality gadgets:

```javascript
async function monitorFinalityGadgets(api) {
  setInterval(async () => {
    try {
      const [beefyHead, grandpaHead] = await Promise.all([
        api.rpc.beefy.getFinalizedHead(),
        api.rpc.chain.getFinalizedHead()
      ]);

      const [beefyBlock, grandpaBlock] = await Promise.all([
        api.rpc.chain.getBlock(beefyHead),
        api.rpc.chain.getBlock(grandpaHead)
      ]);

      const beefyNum = beefyBlock.block.header.number.toNumber();
      const grandpaNum = grandpaBlock.block.header.number.toNumber();
      const lag = grandpaNum - beefyNum;

      console.log(`GRANDPA: #${grandpaNum} | BEEFY: #${beefyNum} | Lag: ${lag} blocks`);
    } catch (error) {
      console.error('Monitor error:', error.message);
    }
  }, 12000);
}
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/astar/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Astar RPC Method

Retrieves complete block information from Astar, including the block header, extrinsics, and justifications.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Astar RPC Method
curl https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/astar/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/astar/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Astar RPC Method

Returns the block hash for a given block number on Astar. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`chain_getBlockHash` is fundamental for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Astar
- **Block Navigation** - Navigate the blockchain history for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Astar
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Astar for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Astar state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Astar network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/astar/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Astar RPC Method

Returns the hash of the last finalized block on Astar. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`chain_getFinalizedHead` is critical for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Astar
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Astar RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://api-astar.n.dwellir.com/YOUR_API_KEY', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Astar:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/astar/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/astar/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHeader - Astar RPC Method

Returns the block header for a given hash on Astar. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`chain_getHeader` is ideal for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Astar
- **Chain Synchronization** - Track block production and monitor chain progress for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Astar:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Astar chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Astar block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/astar/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/astar/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_subscribeFinalizedHeads - Astar RPC Method

Subscribe to receive notifications when blocks are finalized on Astar. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Exchange Deposits** - Only credit funds after finalization for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://api-astar.n.dwellir.com/YOUR_API_KEY`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Astar RPC Method
wscat -c wss://api-astar.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://api-astar.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Astar:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/astar/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/astar/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Astar RPC Method

Subscribe to receive notifications when new block headers are produced on Astar. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`chain_subscribeNewHeads` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Block Monitoring** - Track new blocks in real time on Astar for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://api-astar.n.dwellir.com/YOUR_API_KEY`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Astar RPC Method
wscat -c wss://api-astar.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://api-astar.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Astar as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Astar:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/astar/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/astar/chain_getBlock) - Get full block data with extrinsics
- `chain_unsubscribeNewHeads` - Unsubscribe from new heads

---

## eth_accounts - Astar RPC Method

Returns a list of addresses owned by the client on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/astar/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/astar/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Astar RPC Method

Returns the number of the most recent block on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_blockNumber` is fundamental for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Syncing Applications** - Keep your dApp in sync with the latest Astar blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Astar block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Astar block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Astar block: {block_number}')

# eth_blockNumber - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
print(f'Astar block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Astar block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Astar:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Astar:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Astar node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/astar/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check if node is still syncing

---

## eth_call - Astar RPC Method

Executes a new message call immediately without creating a transaction on Astar. Used for reading smart contract state.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_call` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Astar
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const walletAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Astar. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Astar
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Astar

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    data := common.FromHex("0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Astar RPC Method

Returns the chain ID used for transaction signing on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_chainId` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Astar RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/astar/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check sync status

---

## eth_coinbase - Astar RPC Method

Checks the legacy `eth_coinbase` compatibility method on Astar. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Astar coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Astar coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Astar coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Astar coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Astar coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Astar nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/astar/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/astar/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/astar/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Astar RPC Method

Estimates the gas necessary to execute a transaction on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Astar
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Astar.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Astar may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "to": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Astar RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/astar/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Astar RPC Method

Returns historical gas fee data on Astar, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Astar without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Astar.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Astar) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/astar/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/astar/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Astar RPC Method

Returns the current gas price on Astar in wei.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Astar
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Astar. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Astar, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Astar: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Astar supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Astar RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/astar/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/astar/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Astar RPC Method

Returns the balance of a given address on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getBalance` is fundamental for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Astar
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Astar
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Astar. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Astar`);
  return balance;
}

displayBalance('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Astar at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Astar')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Astar')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Astar.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Astar RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/astar/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/astar/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Astar RPC Method

Returns information about a block by hash on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getBlockByHash` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Astar
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Astar.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Astar RPC Method
verify_block_still_canonical('0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Astar.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Astar RPC Method

Returns information about a block by block number on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getBlockByNumber` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Astar
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Astar with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Astar. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Astar RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/astar/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/astar/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Astar RPC Method

# eth_getBlockReceipts - Astar RPC Method

Returns all transaction receipts for a block on Astar. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getBlockReceipts` is useful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Astar, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Astar. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/astar/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/astar/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Astar RPC Method

Returns the bytecode at a given address on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getCode` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Astar
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Astar:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Astar:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Astar RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/astar/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/astar/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Astar RPC Method

Polls a filter on Astar and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getFilterChanges` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Astar
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Astar RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Astar:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/astar/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/astar/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/astar/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/astar/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/astar/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Astar RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Astar. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getFilterLogs` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Astar
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Astar RPC Method
FILTER_ID=$(curl -s -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/astar/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/astar/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/astar/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Astar RPC Method

# eth_getLogs - Astar RPC Method

Returns an array of all logs matching a given filter object on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Astar for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Astar
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Astar.

```javascript
const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Astar
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Astar

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/astar/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Astar RPC Method

Returns the value from a storage position at a given address on Astar. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getStorageAt` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Astar:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Astar:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/astar/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/astar/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/astar/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Astar RPC Method

# eth_getTransactionByHash - Astar RPC Method

Returns the information about a transaction by transaction hash on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getTransactionByHash` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Astar
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Astar. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Astar. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/astar/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Astar RPC Method

Returns the number of transactions sent from an address on Astar, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getTransactionCount` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Astar
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Astar nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
nonce = get_transaction_count(address)
print(f'Astar nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Astar nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Astar:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/astar/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/astar/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/astar/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Astar RPC Method

# eth_getTransactionReceipt - Astar RPC Method

Returns the receipt of a transaction by transaction hash on Astar. Receipt is only available for mined transactions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_getTransactionReceipt` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Astar and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Astar. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Astar, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x43ac31f47abf56b34e0903e390dd9a1820ee7f03a6359cf2ef08d7152eb26cf3")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/astar/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/astar/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Astar RPC Method

Returns the legacy `eth_hashrate` compatibility value on Astar. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Astar hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Astar hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Astar hashrate: {hashrate} H/s')

# eth_hashrate - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Astar hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Astar hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/astar/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/astar/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Astar RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Astar. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Astar
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Astar priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Astar priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Astar priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Astar:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Astar:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/astar/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/astar/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/astar/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Astar RPC Method

Checks the legacy `eth_mining` compatibility method on Astar. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Astar mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Astar mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Astar mining: {mining["result"]}')

# eth_mining - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Astar mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Astar mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/astar/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/astar/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Astar RPC Method

Creates a filter on Astar that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_newBlockFilter` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Block Monitoring** - Detect new blocks on Astar as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Astar RPC Method
FILTER_ID=$(curl -s -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Astar:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/astar/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/astar/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Astar RPC Method

Creates a filter object on Astar based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_newFilter` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Event Monitoring** - Subscribe to specific contract events on Astar such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Astar:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/astar/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/astar/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/astar/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/astar/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Astar RPC Method

Creates a filter on Astar that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Mempool Monitoring** - Observe unconfirmed transactions on Astar to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Astar RPC Method
FILTER_ID=$(curl -s -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Astar...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Astar:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/astar/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/astar/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Astar RPC Method

Returns the current Ethereum protocol version used by the Astar node.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_protocolVersion` is useful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Astar protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Astar protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Astar protocol version: {version}')

# eth_protocolVersion - Astar RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
print(f'Astar protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Astar protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Astar nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/astar/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/astar/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/astar/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Astar RPC Method

Submits a pre-signed transaction for broadcast to Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Astar
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Astar mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-astar.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/astar/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Astar RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/astar/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/astar/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Astar RPC Method

Creates and sends a new transaction from an unlocked account on Astar. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Astar RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Astar tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Astar tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Astar tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Astar tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Astar tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Astar dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/astar/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/astar/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Astar RPC Method

Signs a transaction with the private key of the specified account on Astar without submitting it to the network.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Astar RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Astar tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Astar tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Astar tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Astar:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/astar/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/astar/eth_accounts) - List accounts available for signing

---

## eth_syncing - Astar RPC Method

# eth_syncing - Astar RPC Method

Returns the sync status of your Astar node - either `false` when fully synced, or an object describing the sync progress.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_syncing` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Astar node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Astar node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Astar node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/astar/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/astar/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/astar/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Astar RPC Method

Removes a filter on Astar that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`eth_uninstallFilter` is important for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Astar RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/astar/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/astar/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/astar/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/astar/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/astar/eth_getFilterLogs) - Get all logs matching a filter

---

## grandpa_roundState - Astar RPC Method

Returns the state of the current GRANDPA finality round on Astar when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`grandpa_roundState` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Finality Monitoring** -- Track whether GRANDPA rounds are progressing normally or stalling on Astar, critical for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Consensus Health Checks** -- Detect finality delays by comparing prevote/precommit counts against the supermajority threshold weight
- **Validator Participation Analysis** -- Monitor which validators are actively voting and whether the authority set has sufficient online weight
- **Authority Set Tracking** -- Observe `setId` changes after validator set rotations to verify smooth authority transitions
- **Capability Detection** -- Confirm whether the shared endpoint exposes GRANDPA round internals before you build monitoring around them

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

- `setId` (`u64, required`): The current GRANDPA authority set ID; increments when the validator set changes
- `best` (`RoundState, required`): State of the best (most recent) active round
- `background` (`Vec<RoundState>, required`): Background rounds that are still being tracked (typically the previous round)
- `round` (`u64, required`): The round number
- `totalWeight` (`u64, required`): Total combined weight of all authorities in this set
- `thresholdWeight` (`u64, required`): Minimum weight required for a supermajority (2/3 + 1 of totalWeight)
- `prevotes` (`Votes, required`): Current prevote state for this round
- `precommits` (`Votes, required`): Current precommit state for this round
- `currentWeight` (`u64, required`): Total weight of votes received so far
- `missing` (`Vec<AuthorityId>, required`): List of authority public keys that have not yet voted

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "setId": 4821,
    "best": {
      "round": 19384,
      "totalWeight": 297,
      "thresholdWeight": 199,
      "prevotes": {
        "currentWeight": 297,
        "missing": []
      },
      "precommits": {
        "currentWeight": 264,
        "missing": [
          "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
          "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
        ]
      }
    },
    "background": []
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  const roundState = await api.rpc.grandpa.roundState();
  const best = roundState.best;

  console.log('Authority set ID:', roundState.setId.toString());
  console.log('Round:', best.round.toString());
  console.log('Total weight:', best.totalWeight.toString());
  console.log('Threshold weight:', best.thresholdWeight.toString());
  console.log('Prevote weight:', best.prevotes.currentWeight.toString());
  console.log('Precommit weight:', best.precommits.currentWeight.toString());
  console.log('Missing precommits:', best.precommits.missing.length);
} catch (error) {
  console.log('grandpa_roundState unsupported:', error.message);
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('grandpa_roundState unsupported:', payload.error.message);
} else {
  console.log('Set ID:', payload.result.setId);
  console.log('Best round:', payload.result.best.round);
  console.log('Prevote progress:', payload.result.best.prevotes.currentWeight, '/', payload.result.best.thresholdWeight);
  console.log('Precommit progress:', payload.result.best.precommits.currentWeight, '/', payload.result.best.thresholdWeight);
}
```

```python
import requests

def get_grandpa_round_state():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'grandpa_roundState',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

try:
    state = get_grandpa_round_state()
    best = state['best']

    print(f"Authority set ID: {state['setId']}")
    print(f"Round: {best['round']}")
    print(f"Prevote: {best['prevotes']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Precommit: {best['precommits']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Missing precommit voters: {len(best['precommits']['missing'])}")
except KeyError:
    print('grandpa_roundState unsupported on this endpoint')

# grandpa_roundState - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
response = substrate.rpc_request('grandpa_roundState', [])
if 'error' in response:
    print(f"grandpa_roundState unsupported: {response['error']['message']}")
else:
    print(f"Set ID: {response['result']['setId']}, Round: {response['result']['best']['round']}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
    set_id: u64,
    best: BestRound,
    background: Vec<BestRound>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct BestRound {
    round: u64,
    total_weight: u64,
    threshold_weight: u64,
    prevotes: Votes,
    precommits: Votes,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Votes {
    current_weight: u64,
    missing: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "grandpa_roundState",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    if body.get("error").is_some() {
        println!("grandpa_roundState unsupported: {}", body["error"]["message"]);
        return Ok(());
    }

    let state: RoundState = serde_json::from_value(body["result"].clone())?;

    println!("Set ID: {}", state.set_id);
    println!("Round: {}", state.best.round);
    println!("Prevote: {}/{}", state.best.prevotes.current_weight, state.best.threshold_weight);
    println!("Precommit: {}/{}", state.best.precommits.current_weight, state.best.threshold_weight);
    println!("Missing precommit voters: {}", state.best.precommits.missing.len());

    Ok(())
}
```

## Common Use Cases

### 1. Finality Health Monitoring

Periodically check whether GRANDPA rounds are progressing and alert on stalls:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorFinality(api, intervalMs = 10000) {
  let lastRound = 0;
  let lastSetId = 0;
  let stallCount = 0;

  setInterval(async () => {
    const state = await api.rpc.grandpa.roundState();
    const best = state.best;
    const round = best.round.toNumber();
    const setId = state.setId.toNumber();
    const prevoteProgress = best.prevotes.currentWeight.toNumber();
    const precommitProgress = best.precommits.currentWeight.toNumber();
    const threshold = best.thresholdWeight.toNumber();

    if (setId !== lastSetId) {
      console.log(`Authority set changed: ${lastSetId} -> ${setId}`);
      lastSetId = setId;
    }

    if (round === lastRound) {
      stallCount++;
      if (stallCount >= 3) {
        console.warn(`GRANDPA round ${round} stalled for ${stallCount} checks`);
        console.warn(`  Prevotes: ${prevoteProgress}/${threshold}`);
        console.warn(`  Precommits: ${precommitProgress}/${threshold}`);
        console.warn(`  Missing voters: ${best.precommits.missing.length}`);
      }
    } else {
      stallCount = 0;
      console.log(`Round ${round} | prevotes=${prevoteProgress}/${threshold} precommits=${precommitProgress}/${threshold}`);
    }

    lastRound = round;
  }, intervalMs);
}
```

### 2. Validator Participation Report

Generate a report of which validators are consistently missing votes:

```javascript
async function trackMissingVoters(api, samples = 20, delayMs = 6000) {
  const missingCounts = {};

  for (let i = 0; i < samples; i++) {
    const state = await api.rpc.grandpa.roundState();
    const missing = state.best.precommits.missing;

    missing.forEach((authority) => {
      const key = authority.toString();
      missingCounts[key] = (missingCounts[key] || 0) + 1;
    });

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  // Sort by most frequently missing
  const sorted = Object.entries(missingCounts)
    .sort(([, a], [, b]) => b - a);

  console.log('Validator participation report:');
  sorted.forEach(([authority, count]) => {
    const missRate = ((count / samples) * 100).toFixed(1);
    console.log(`  ${authority}: missed ${count}/${samples} (${missRate}%)`);
  });

  return sorted;
}
```

### 3. Supported-Fallback Check

If the endpoint does not expose GRANDPA round internals, fall back to finalized-head tracking:

```javascript
async function getFinalitySignal(api) {
  try {
    return { supported: true, roundState: await api.rpc.grandpa.roundState() };
  } catch (error) {
    return {
      supported: false,
      finalizedHead: (await api.rpc.chain.getFinalizedHead()).toHex(),
      message: error.message
    };
  }
}
```

### 3. Finality Lag Detection

Compare the finalized head with the best block to measure finality lag:

```javascript
async function getFinalityLag(api) {
  const [roundState, finalizedHash, bestHeader] = await Promise.all([
    api.rpc.grandpa.roundState(),
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader()
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);
  const bestNumber = bestHeader.number.toNumber();
  const finalizedNumber = finalizedHeader.number.toNumber();
  const lag = bestNumber - finalizedNumber;

  return {
    bestBlock: bestNumber,
    finalizedBlock: finalizedNumber,
    lagBlocks: lag,
    grandpaRound: roundState.best.round.toNumber(),
    setId: roundState.setId.toNumber(),
    prevoteReached: roundState.best.prevotes.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber(),
    precommitReached: roundState.best.precommits.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber()
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/astar/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/astar/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/astar/system_health) -- Check overall node health including sync and peer status

---

## net_listening - Astar RPC Method

Checks whether the connected Astar client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`net_listening` is useful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Astar node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Astar node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Astar RPC Method
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Astar node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/astar/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/astar/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/astar/web3_clientVersion) - Get node client info

---

## net_peerCount - Astar RPC Method

Returns the number of peers currently connected to your Astar node.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`net_peerCount` is important for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Astar deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Astar peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Astar peers: {peer_count}')

# net_peerCount - Astar RPC Method
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Astar peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Astar nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/astar/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/astar/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/astar/web3_clientVersion) - Get node client info

---

## net_version - Astar RPC Method

Returns the current network ID on Astar as a decimal string. The network ID identifies which network the node is connected to.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`net_version` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Endpoint Identification** - Confirm your application is connected to the expected Astar network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Astar network ID:', networkId);

// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Astar network ID: {network_id}')

# net_version - Astar RPC Method
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Astar network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/astar/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/astar/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/astar/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check node sync progress

---

## payment_queryFeeDetails - Astar RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Astar. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`payment_queryFeeDetails` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Astar
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/astar/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/astar/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/astar/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/astar/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Astar RPC Method

Estimates the fee for an encoded extrinsic on Astar. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`payment_queryInfo` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Astar
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Astar RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/astar/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/astar/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/astar/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/astar/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Astar RPC Method

Returns a list of all RPC methods exposed by the Astar node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`rpc_methods` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Astar node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Astar endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://api-astar.n.dwellir.com/YOUR_API_KEY', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Astar endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/astar/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/astar/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Astar RPC Method

Calls a runtime API function on Astar and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_call` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/astar/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/astar/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/astar/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/astar/system_version) -- Get the node version for compatibility checking

---

## state_getKeys

# state_getKeys

## Description

Returns storage keys with a given prefix. Because Astar staking maps can be large, use this method sparingly and follow with `state_getKeysPaged` for controlled pagination.

## Parameters

| Position       | Type   | Description                                  |
| -------------- | ------ | -------------------------------------------- |
| 0              | string | Storage key prefix in hex                    |
| 1 *(optional)* | string | Block hash                                   |
| 2 *(optional)* | number | Result limit (ignored on some node versions) |

## Request Example

Get collator session keys by prefixing `Session.NextKeys` (no account parameter).

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3",
    null,
    5
  ],
  "id": 7
}
```

## Response Example

```json
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": [
    "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30165c612f75544f29a2b2224d8a6944df8f70196c856ae141ecf1aeb3de10d54461317f2fdeae21b",
    "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb302a1a4c9e333badf063b6446a3e6b0781d73ea137f9c74f59918312cb7938a1f234287ea08418633",
    "…"
  ]
}
```

If a node returns `Response is too big`, switch to `state_getKeysPaged` with smaller page sizes.

## Code Examples

Python
JavaScript

```python
substrate = SubstrateInterface(url="wss://api-astar.n.dwellir.com/YOUR_API_KEY", ss58_format=5)
prefix = substrate.generate_storage_hash('Session', 'NextKeys')
keys = substrate.rpc_request('state_getKeys', [prefix, None, 5])['result']
print(keys[:3])
```

```typescript
const prefix = api.query.session.nextKeys.keyPrefix();
const keys = await api.rpc.state.getKeys(prefix.toHex());
console.log(`Returned ${keys.length} keys`);
```

---

## state_getKeysPaged - Astar RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Astar. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_getKeysPaged` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Astar
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://api-astar.n.dwellir.com/YOUR_API_KEY";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/astar/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/astar/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getMetadata - Astar RPC Method

Returns the runtime metadata for Astar as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_getMetadata` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Astar
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Astar RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Astar:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Astar:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/astar/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/astar/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) - Call runtime APIs described in metadata

---

## state_getRuntimeVersion - Astar RPC Method

# state_getRuntimeVersion - Astar RPC Method

Returns the runtime version information for Astar, including the spec name, spec version, implementation version, and supported API versions.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_getRuntimeVersion` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Astar
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Astar in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/astar/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/astar/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Astar RPC Method

Returns the SCALE-encoded storage value for a given key on Astar. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_getStorage` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Astar
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Astar RPC Method
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/astar/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/astar/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call runtime APIs for computed state that is not directly in storage
- `state_subscribeStorage` -- Subscribe to storage changes in real time via WebSocket

---

## state_queryStorageAt - Astar RPC Method

Queries multiple storage keys at a specific block on Astar, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`state_queryStorageAt` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Astar
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Astar RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/astar/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/astar/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/astar/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/astar/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## system_chain - Astar RPC Method

Returns the chain name of the Astar network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`system_chain` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Network Verification** -- Confirm your application is connected to the correct Astar network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Astar"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://api-astar.n.dwellir.com/YOUR_API_KEY', 'Astar');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/astar/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/astar/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/astar/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/astar/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/astar/rpc_methods) -- List all available RPC methods

---

## system_health - Astar RPC Method

# system_health - Astar RPC Method

Returns the health status of the Astar node, including peer count, sync state, and whether the node expects to have peers.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`system_health` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Astar
- **Load Balancing** - Route requests only to healthy, fully synced nodes for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://api-astar.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-astar.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Astar:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Astar nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://api-astar.n.dwellir.com/YOUR_API_KEY')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/astar/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) - Get chain name
- `system_syncState` - Get detailed sync progress
- `system_peers` - Get detailed peer information

---

## system_name - Astar RPC Method

Returns the node implementation name on Astar. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`system_name` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Astar
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Astar node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Astar node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Astar node implementation: {name}')

# system_name - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
name = substrate.rpc_request('system_name', [])['result']
print(f'Astar node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Astar node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Astar nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/astar/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/astar/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/astar/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/astar/rpc_methods) -- List all available RPC methods

---

## system_properties - Astar RPC Method

Returns the chain-specific properties for Astar, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`system_properties` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Astar
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/astar/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/astar/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/astar/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/astar/rpc_methods) -- List all available RPC methods

---

## system_version - Astar RPC Method

Returns the node implementation version string on Astar. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`system_version` is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Astar
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for cross-chain DeFi, multi-VM smart contracts, and XCM-enabled interoperability with Ethereum and Cosmos
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-astar.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Astar node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Astar node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://api-astar.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Astar node version: {version}')

# system_version - Astar RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-astar.n.dwellir.com/YOUR_API_KEY')
version = substrate.rpc_request('system_version', [])['result']
print(f'Astar node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-astar.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Astar node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Astar nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/astar/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/astar/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/astar/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/astar/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/astar/rpc_methods) -- List all available RPC methods

---

## txpool_content - Astar RPC Method

# txpool_content - Astar RPC Method

Returns the full pending and queued transaction pool for the connected Astar endpoint. Transactions are grouped first by sender address and then by nonce.

> **Non-standard method.** `txpool_content` is a Geth-style mempool inspection method. It is not part of the core Ethereum Execution API method set, and many shared RPC endpoints disable it because of response size and sensitivity concerns.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`txpool_content` is useful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Replacement Transaction Debugging** - Inspect multiple transactions with the same sender and nonce
- **Mempool Analytics** - Analyze which accounts dominate pending flow and how backlogs are distributed
- **Relayer Operations** - Verify whether submitted transactions are still pending, queued, or replaced
- **Fee Strategy Tuning** - Inspect real mempool fee levels and transaction types across the pending pool

## Best Practices

- Response can be very large on congested networks; be prepared to handle large payloads
- Use txpool\_status for summary statistics instead when you do not need full content
- Filter results client-side for specific addresses of interest to reduce noise
- This is a non-standard method; many shared endpoints disable this for performance reasons

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "txpool_content",
  "params": [],
  "id": 1
}
```

## Response Fields

- `pending` (`Object, required`): Address-indexed map of processable transactions grouped by nonce
- `queued` (`Object, required`): Address-indexed map of non-processable transactions grouped by nonce

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "pending": {
      "0x000000cd5e2aa28b0fbb66219756f36e318a4ed7": {
        "94": {
          "from": "0x000000cd5e2aa28b0fbb66219756f36e318a4ed7",
          "to": "0xe88b4ac89a986048e48e48ff019eee4281a9791f",
          "nonce": "0x5e",
          "gas": "0x7a120",
          "gasPrice": "0x5d21dba00",
          "maxFeePerGas": "0x5d21dba00",
          "maxPriorityFeePerGas": "0x5d21dba00",
          "hash": "0xeb0eb7b61fd61739bfd87667fd787eca81d3af18bb67eaced20a7e1c0f798532",
          "value": "0x0",
          "type": "0x2"
        }
      }
    },
    "queued": {}
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "txpool_content",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const txpool = await provider.send('txpool_content', []);
const pendingAccounts = Object.keys(txpool.pending);
const queuedAccounts = Object.keys(txpool.queued);

console.log('Pending accounts:', pendingAccounts.length);
console.log('Queued accounts:', queuedAccounts.length);

if (pendingAccounts.length > 0) {
  const firstAccount = pendingAccounts[0];
  const firstNonce = Object.keys(txpool.pending[firstAccount])[0];
  console.log('Sample pending tx:', txpool.pending[firstAccount][firstNonce]);
}
```

```python
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'txpool_content',
        'params': [],
        'id': 1,
    },
)

txpool = response.json()['result']
pending_accounts = list(txpool['pending'].keys())
queued_accounts = list(txpool['queued'].keys())

print('Pending accounts:', len(pending_accounts))
print('Queued accounts:', len(queued_accounts))
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var txpool map[string]map[string]map[string]map[string]any
    err = client.CallContext(context.Background(), &txpool, "txpool_content")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending accounts: %d\n", len(txpool["pending"]))
    fmt.Printf("Queued accounts: %d\n", len(txpool["queued"]))
}
```

## Related Methods

- [`txpool_status`](https://www.dwellir.com/docs/astar/txpool_status) - Retrieve only pending and queued counters
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/astar/eth_getTransactionByHash) - Inspect a single transaction after you identify it in the mempool
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/astar/eth_sendRawTransaction) - Broadcast signed transactions to the network

---

## txpool_status - Astar RPC Method

# txpool_status - Astar RPC Method

Returns transaction pool counters for the connected Astar endpoint. The result separates transactions that are immediately processable (`pending`) from those waiting on an earlier nonce or other prerequisite (`queued`).

> **Non-standard method.** `txpool_status` is a Geth-style mempool inspection method. It is not part of the core Ethereum Execution API method set, and many shared RPC endpoints disable it.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`txpool_status` is valuable for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Mempool Monitoring** - Watch pending versus queued pressure on a node
- **Congestion Signals** - Detect bursts of transaction backlog before they show up in block-level metrics
- **Node Health Checks** - Confirm a node is accepting and classifying new transactions as expected
- **Operational Dashboards** - Surface lightweight txpool counters without pulling full transaction content

## Best Practices

- Returns pending and queued transaction counts; high pending counts suggest network congestion
- Combine with eth\_gasPrice for informed transaction submission timing decisions
- This is a non-standard method; many shared endpoints disable txpool access
- Use as a lightweight alternative to txpool\_content when you only need aggregate counts

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "txpool_status",
  "params": [],
  "id": 1
}
```

## Response Fields

- `pending` (`QUANTITY, required`): Number of processable transactions currently in the pool
- `queued` (`QUANTITY, required`): Number of transactions waiting on an earlier prerequisite such as nonce order

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "pending": "0xbaf3",
    "queued": "0x1b6"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "txpool_status",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const txpool = await provider.send('txpool_status', []);

console.log('Pending:', parseInt(txpool.pending, 16));
console.log('Queued:', parseInt(txpool.queued, 16));
```

```python
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'txpool_status',
        'params': [],
        'id': 1,
    },
)

txpool = response.json()['result']
print('Pending:', int(txpool['pending'], 16))
print('Queued:', int(txpool['queued'], 16))
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var status map[string]string
    err = client.CallContext(context.Background(), &status, "txpool_status")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %s\n", status["pending"])
    fmt.Printf("Queued: %s\n", status["queued"])
}
```

## Related Methods

- [`txpool_content`](https://www.dwellir.com/docs/astar/txpool_content) - Inspect the full pending and queued transaction maps
- [`eth_blockNumber`](https://www.dwellir.com/docs/astar/eth_blockNumber) - Track block production alongside mempool pressure
- [`eth_gasPrice`](https://www.dwellir.com/docs/astar/eth_gasPrice) - Compare congestion signals with fee estimates

---

## web3_clientVersion - Astar RPC Method

Returns the current client software version string for your Astar node, including the client name, version number, OS, and runtime.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

## When to Use This Method

`web3_clientVersion` is valuable for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Astar client:', clientVersion);

// Using fetch
const response = await fetch('https://api-astar.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Astar client: {client_version}')

# web3_clientVersion - Astar RPC Method
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Astar client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/astar/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/astar/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/astar/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/astar/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Astar RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Astar.

> **Why Astar?** Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-astar.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Astar RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-astar.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-astar.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-astar.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/astar/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/astar/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/astar/web3_clientVersion) - Get node client version

---

## Avalanche - High-Performance Blockchain Platform

# Avalanche - High-Performance Blockchain Platform

## Why Build on Avalanche?

Avalanche is a high-performance blockchain platform that delivers sub-second finality and supports custom blockchain networks. Built on the innovative Avalanche consensus mechanism, it offers:

### **Lightning Fast Performance**

- **Sub-second finality** - Transactions confirm in under 1 second
- **4,500+ TPS** - Industry-leading throughput capacity
- **Low fees** - Cost-effective transactions with predictable pricing

### **Unique Three-Chain Architecture**

- **X-Chain** - Exchange Chain for asset creation and trading
- **P-Chain** - Platform Chain for validator coordination and subnets
- **C-Chain** - Contract Chain for Ethereum-compatible smart contracts

### **Enterprise Security**

- **Avalanche Consensus** - Novel consensus protocol with strong safety guarantees
- **Validator Network** - Decentralized network of validators securing the platform
- **Battle-tested** - Processing millions of transactions since mainnet launch

### **Thriving Ecosystem**

- **400+ projects** - Growing DeFi, Gaming, and NFT ecosystem
- **EVM Compatible** - Full Ethereum compatibility on C-Chain
- **Subnet Support** - Create custom blockchain networks

## Quick Start with Avalanche C-Chain

Connect to Avalanche C-Chain in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Avalanche C-Chain mainnet
const provider = new JsonRpcProvider(
  'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Avalanche C-Chain mainnet
const web3 = new Web3(
  'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Avalanche:', chainId === 43114);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';
import { avalanche } from 'viem/chains';

// Create Avalanche client
const client = createPublicClient({
  chain: avalanche,
  transport: http('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

## Network Information

| Parameter    | Value     | Details      |
| ------------ | --------- | ------------ |
| Chain ID     | 43114     | Mainnet      |
| Block Time   | 2 seconds | Average      |
| Gas Token    | AVAX      | Native token |
| RPC Standard | Ethereum  | JSON-RPC 2.0 |

## API Reference

Avalanche C-Chain supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/) with sub-second finality and high throughput.

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // Avalanche specific: Fast finality means quick confirmations
  console.log('Transaction confirmed in block:', receipt.blockNumber);

  return receipt;
}
```

### Fast Finality Optimization

Leverage Avalanche's sub-second finality:

```javascript
// Avalanche transactions finalize quickly
async function fastConfirmation(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // On Avalanche, 1 confirmation is typically sufficient
  if (receipt.blockNumber) {
    console.log('Transaction finalized with 1 confirmation');
    return receipt;
  }
}
```

### Event Filtering

Efficiently query contract events:

```javascript
// Query events with optimal batch size for Avalanche
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 5000; // Avalanche recommended batch size

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);
  }

  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine multiple RPC calls for optimal performance:

```javascript
const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] }
];

const results = await provider.send(batch);
```

### 2. **Connection Pooling**

Reuse provider instances to minimize connection overhead:

```javascript
// Singleton pattern for provider
class AvalancheProvider {
  static instance = null;

  static getInstance() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'
      );
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data to reduce API calls:

```javascript
const cache = new Map();

async function getCachedBlockData(blockNumber) {
  const key = `block_${blockNumber}`;

  if (!cache.has(key)) {
    const block = await provider.getBlock(blockNumber);
    cache.set(key, block);
  }

  return cache.get(key);
}
```

## Troubleshooting Common Issues

### Error: "Gas required exceeds allowance"

Avalanche uses dynamic gas pricing. Always estimate gas properly:

```javascript
// Get current fee data
const feeData = await provider.getFeeData();

const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: await provider.estimateGas({
    to: recipient,
    value: amount
  })
};
```

### Error: "Transaction underpriced"

Avalanche uses EIP-1559 pricing. Use dynamic gas pricing:

```javascript
// Get current network conditions
const feeData = await provider.getFeeData();

const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: 21000n
};
```

### Error: "Rate limit exceeded"

Implement exponential backoff for resilient applications:

```javascript
async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 2 ** i * 1000));
      } else {
        throw error;
      }
    }
  }
}
```

## Migration Guide

### From Ethereum Mainnet

Moving from L1 to Avalanche C-Chain is seamless:

```javascript
// Before (Ethereum)
const provider = new JsonRpcProvider('https://eth-rpc.example.com');

// After (Avalanche)
const provider = new JsonRpcProvider(
  'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'
);

// Smart contracts work identically
// Same tooling and libraries
// Native token is AVAX instead of ETH
// Note: Different chain ID (43114)
// Note: Much faster finality (~1 second)
```

### From Other EVM Chains

Avalanche C-Chain is fully EVM compatible:

```javascript
// Same contract deployment process
const contractFactory = new ContractFactory(abi, bytecode, signer);
const contract = await contractFactory.deploy(...constructorArgs);

// Wait for deployment (much faster on Avalanche)
await contract.waitForDeployment();
```

## Resources & Tools

### Official Resources

- [Avalanche Documentation](https://docs.avax.network)
- [Avalanche Explorer](https://snowtrace.io)
- [Avalanche Bridge](https://bridge.avax.network)

### Developer Tools

- [Hardhat Config](https://build.avax.network/docs/dapps/toolchains/hardhat)
- [Foundry Setup](https://build.avax.network/docs/dapps/toolchains/foundry)
- [Core Wallet](https://core.app)

### Ecosystem

- [DeFi Llama](https://defillama.com/chain/Avalanche) - Track Avalanche DeFi
- [Avalanche Website](https://www.avax.network/) - Discover projects and ecosystem
- [Subnets](https://subnets.avax.network) - Custom blockchain networks

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

### Related Reading

- [Best Avalanche RPC Providers 2026](https://www.dwellir.com/blog/top-avalanche-rpc-providers)

***

*Start building on Avalanche with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## debug_traceBlock - Avalanche RPC Method

Traces all transactions in a block on Avalanche by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Avalanche - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Avalanche
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Avalanche RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Avalanche:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Avalanche blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/avalanche/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/avalanche/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Avalanche RPC Method

Traces all transactions in a block on Avalanche identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Avalanche - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Avalanche
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Avalanche RPC Method
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const blockHash = '0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Avalanche:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Avalanche:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/avalanche/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/avalanche/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/avalanche/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/avalanche/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Avalanche RPC Method

Traces all transactions in a block on Avalanche identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Avalanche - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Avalanche execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Avalanche RPC Method
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Avalanche:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Avalanche:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/avalanche/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/avalanche/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/avalanche/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Avalanche RPC Method

Traces a call on Avalanche without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Avalanche - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Avalanche
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Avalanche RPC Method
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
        "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
        "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    data: '0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    data: '0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    'data': '0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
        "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Avalanche to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Avalanche:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Avalanche:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/avalanche/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/avalanche/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Avalanche RPC Method

Traces a transaction execution on Avalanche by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Avalanche
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const txHash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

tx_hash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4'

# debug_traceTransaction - Avalanche RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/avalanche/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/avalanche/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Avalanche RPC Method

Returns a list of addresses owned by the client on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for enterprise developers, RWA tokenizers, and teams building custom blockchain networks in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/avalanche/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/avalanche/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Avalanche RPC Method

Returns the number of the most recent block on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_blockNumber` is fundamental for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Syncing Applications** - Keep your dApp in sync with the latest Avalanche blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Avalanche block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const blockNumber = await provider.getBlockNumber();
console.log('Avalanche block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Avalanche block: {block_number}')

# eth_blockNumber - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
print(f'Avalanche block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Avalanche block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Avalanche:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Avalanche:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Avalanche node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/avalanche/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check if node is still syncing

---

## eth_call - Avalanche RPC Method

Executes a new message call immediately without creating a transaction on Avalanche. Used for reading smart contract state.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_call` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Avalanche
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const tokenAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';
const walletAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Avalanche. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const routerAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Avalanche
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Avalanche

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Avalanche RPC Method
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "data": "0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    data := common.FromHex("0x70a0823100000000000000000000000009383137c1eee3e1a8bc781228e4199f6b4a9bbf")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Avalanche RPC Method

Returns the chain ID used for transaction signing on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_chainId` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Avalanche RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/avalanche/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check sync status

---

## eth_coinbase - Avalanche RPC Method

Checks the legacy `eth_coinbase` compatibility method on Avalanche. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for enterprise developers, RWA tokenizers, and teams building custom blockchain networks when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Avalanche coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Avalanche coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Avalanche coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
try:
    print(f'Avalanche coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Avalanche coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Avalanche nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/avalanche/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/avalanche/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/avalanche/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Avalanche RPC Method

Estimates the gas necessary to execute a transaction on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Avalanche
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Avalanche.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Avalanche may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "to": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Avalanche RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/avalanche/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Avalanche RPC Method

Returns historical gas fee data on Avalanche, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Avalanche without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Avalanche.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Avalanche) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/avalanche/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/avalanche/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Avalanche RPC Method

Returns the current gas price on Avalanche in wei.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Avalanche
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Avalanche. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Avalanche, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Avalanche: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Avalanche supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Avalanche RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/avalanche/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/avalanche/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Avalanche RPC Method

Returns the balance of a given address on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getBalance` is fundamental for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Avalanche
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Avalanche
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Avalanche. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Avalanche`);
  return balance;
}

displayBalance('0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Avalanche at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Avalanche')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Avalanche')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Avalanche.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    address := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Avalanche RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/avalanche/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/avalanche/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Avalanche RPC Method

Returns information about a block by hash on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getBlockByHash` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Avalanche
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on the fastest smart contract platform with sub-second finality and customizable L1 subnets
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Avalanche.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on the fastest smart contract platform with sub-second finality and customizable L1 subnets.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Avalanche RPC Method
verify_block_still_canonical('0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Avalanche.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    knownHash := common.HexToHash("0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const blockHash = '0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

block_hash = '0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Avalanche RPC Method

Returns information about a block by block number on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getBlockByNumber` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Avalanche
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on the fastest smart contract platform with sub-second finality and customizable L1 subnets

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Avalanche with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Avalanche. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on the fastest smart contract platform with sub-second finality and customizable L1 subnets expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

# eth_getBlockByNumber - Avalanche RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/avalanche/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/avalanche/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Avalanche RPC Method

# eth_getBlockReceipts - Avalanche RPC Method

Returns all transaction receipts for a block on Avalanche. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getBlockReceipts` is useful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Avalanche, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Avalanche. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on the fastest smart contract platform with sub-second finality and customizable L1 subnets, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xb40d80aebafa6c7598ca8bc978354fa1c44f34b08c3f328882b714cb533615bb",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/avalanche/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/avalanche/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Avalanche RPC Method

Returns the bytecode at a given address on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getCode` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Avalanche
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Avalanche:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Avalanche:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Avalanche RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/avalanche/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/avalanche/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Avalanche RPC Method

Polls a filter on Avalanche and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getFilterChanges` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Avalanche
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Avalanche RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Avalanche:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/avalanche/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/avalanche/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/avalanche/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/avalanche/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/avalanche/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Avalanche RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Avalanche. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getFilterLogs` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Avalanche
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Avalanche RPC Method
FILTER_ID=$(curl -s -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/avalanche/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/avalanche/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/avalanche/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Avalanche RPC Method

# eth_getLogs - Avalanche RPC Method

Returns an array of all logs matching a given filter object on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Avalanche for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Avalanche
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const tokenAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Avalanche.

```javascript
const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Avalanche
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Avalanche

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/avalanche/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Avalanche RPC Method

Returns the value from a storage position at a given address on Avalanche. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getStorageAt` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Avalanche:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Avalanche:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/avalanche/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/avalanche/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/avalanche/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Avalanche RPC Method

# eth_getTransactionByHash - Avalanche RPC Method

Returns the information about a transaction by transaction hash on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getTransactionByHash` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Avalanche
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on the fastest smart contract platform with sub-second finality and customizable L1 subnets

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Avalanche. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Avalanche. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on the fastest smart contract platform with sub-second finality and customizable L1 subnets.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    txHash := common.HexToHash("0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const txHash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

tx_hash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/avalanche/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Avalanche RPC Method

Returns the number of transactions sent from an address on Avalanche, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getTransactionCount` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Avalanche
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Avalanche nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf'
nonce = get_transaction_count(address)
print(f'Avalanche nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Avalanche nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Avalanche:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/avalanche/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/avalanche/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/avalanche/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Avalanche RPC Method

# eth_getTransactionReceipt - Avalanche RPC Method

Returns the receipt of a transaction by transaction hash on Avalanche. Receipt is only available for mined transactions.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_getTransactionReceipt` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Avalanche and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on the fastest smart contract platform with sub-second finality and customizable L1 subnets by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Avalanche. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Avalanche, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on the fastest smart contract platform with sub-second finality and customizable L1 subnets. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")

    txHash := common.HexToHash("0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on the fastest smart contract platform with sub-second finality and customizable L1 subnets
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const txHash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

tx_hash = '0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xfe055919188f8e2360bead9ecd97ffa782c610730e9084c6533f799bafafa7e4")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/avalanche/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/avalanche/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Avalanche RPC Method

Returns the legacy `eth_hashrate` compatibility value on Avalanche. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Avalanche hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Avalanche hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Avalanche hashrate: {hashrate} H/s')

# eth_hashrate - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
try:
    print(f'Avalanche hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Avalanche hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/avalanche/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/avalanche/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Avalanche RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Avalanche. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Avalanche
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Avalanche priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Avalanche priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Avalanche priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Avalanche:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Avalanche:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/avalanche/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/avalanche/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/avalanche/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Avalanche RPC Method

Checks the legacy `eth_mining` compatibility method on Avalanche. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Avalanche mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Avalanche mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Avalanche mining: {mining["result"]}')

# eth_mining - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
try:
    print(f'Avalanche mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Avalanche mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/avalanche/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/avalanche/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Avalanche RPC Method

Creates a filter on Avalanche that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_newBlockFilter` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Block Monitoring** - Detect new blocks on Avalanche as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Avalanche RPC Method
FILTER_ID=$(curl -s -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Avalanche:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/avalanche/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/avalanche/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Avalanche RPC Method

Creates a filter object on Avalanche based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_newFilter` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Event Monitoring** - Subscribe to specific contract events on Avalanche such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Avalanche RPC Method
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Avalanche:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/avalanche/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/avalanche/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/avalanche/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/avalanche/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Avalanche RPC Method

Creates a filter on Avalanche that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Mempool Monitoring** - Observe unconfirmed transactions on Avalanche to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for institutional RWA tokenization ($18B+ transfer volume), gaming subnets, and enterprise blockchains

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Avalanche RPC Method
FILTER_ID=$(curl -s -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Avalanche...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Avalanche:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/avalanche/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/avalanche/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Avalanche RPC Method

Returns the current Ethereum protocol version used by the Avalanche node.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_protocolVersion` is useful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Avalanche protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Avalanche protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Avalanche protocol version: {version}')

# eth_protocolVersion - Avalanche RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
print(f'Avalanche protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Avalanche protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Avalanche nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/avalanche/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/avalanche/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Avalanche RPC Method

Submits a pre-signed transaction for broadcast to Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Avalanche
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Avalanche mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/avalanche/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Avalanche RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0x09383137c1eee3e1a8bc781228e4199f6b4a9bbf")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/avalanche/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/avalanche/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Avalanche RPC Method

Creates and sends a new transaction from an unlocked account on Avalanche. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Avalanche RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Avalanche tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Avalanche tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Avalanche tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Avalanche tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Avalanche tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Avalanche dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/avalanche/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/avalanche/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Avalanche RPC Method

Signs a transaction with the private key of the specified account on Avalanche without submitting it to the network.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for enterprise developers, RWA tokenizers, and teams building custom blockchain networks in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Avalanche RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Avalanche tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Avalanche tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Avalanche tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Avalanche:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/avalanche/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/avalanche/eth_accounts) - List accounts available for signing

---

## eth_syncing - Avalanche RPC Method

# eth_syncing - Avalanche RPC Method

Returns the sync status of your Avalanche node - either `false` when fully synced, or an object describing the sync progress.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_syncing` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Avalanche node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Avalanche node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Avalanche node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/avalanche/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/avalanche/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/avalanche/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Avalanche RPC Method

Removes a filter on Avalanche that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`eth_uninstallFilter` is important for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

# eth_uninstallFilter - Avalanche RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/avalanche/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/avalanche/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/avalanche/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/avalanche/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/avalanche/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Avalanche RPC Method

Checks whether the connected Avalanche client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`net_listening` is useful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Avalanche node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

try:
    listening = w3.net.listening
    print(f'Avalanche node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Avalanche RPC Method
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Avalanche node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/avalanche/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/avalanche/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Get node client info

---

## net_peerCount - Avalanche RPC Method

Returns the number of peers currently connected to your Avalanche node.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`net_peerCount` is important for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Avalanche deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Avalanche peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

peer_count = w3.net.peer_count
print(f'Avalanche peers: {peer_count}')

# net_peerCount - Avalanche RPC Method
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Avalanche peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Avalanche nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/avalanche/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/avalanche/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Get node client info

---

## net_version - Avalanche RPC Method

Returns the current network ID on Avalanche as a decimal string. The network ID identifies which network the node is connected to.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`net_version` is essential for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Endpoint Identification** - Confirm your application is connected to the expected Avalanche network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const networkId = await provider.send('net_version', []);
console.log('Avalanche network ID:', networkId);

// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

network_id = w3.net.version
print(f'Avalanche network ID: {network_id}')

# net_version - Avalanche RPC Method
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Avalanche network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/avalanche/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/avalanche/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/avalanche/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check node sync progress

---

## rpc_modules - Avalanche RPC Method

# rpc_modules - Avalanche RPC Method

Returns the enabled JSON-RPC namespaces exposed by the connected Avalanche endpoint together with their version strings.

> **Non-standard method.** `rpc_modules` is a client-introspection RPC that is commonly available on Geth-compatible stacks, but it is not part of the core Ethereum Execution API method set. Availability varies by client and operator policy.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`rpc_modules` is useful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Capability Discovery** - Detect whether namespaces like `debug`, `trace`, `txpool`, or `erigon` are exposed before attempting those calls
- **Client Diagnostics** - Verify what the serving node has enabled when debugging environment-specific issues
- **Infrastructure Audits** - Compare public and private endpoints to confirm which RPC surfaces are intentionally exposed
- **Runtime Feature Gating** - Adjust tooling behavior dynamically based on the actual namespaces available on a node

## Best Practices

- Call at startup to determine which features are available on a node
- Module availability varies by node client and provider configuration
- Use to gate feature access in applications before attempting unsupported calls
- This is a non-standard method; some endpoints may not expose it

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_modules",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Object, required`): Object whose keys are enabled namespaces and whose values are version strings

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "eth": "1.0",
    "net": "1.0",
    "web3": "1.0",
    "rpc": "1.0",
    "debug": "1.0",
    "trace": "1.0",
    "txpool": "1.0"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_modules",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const modules = await provider.send('rpc_modules', []);
console.log('Namespaces:', Object.keys(modules));

if (modules.debug) {
  console.log('Debug RPC is enabled');
}
```

```python
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'rpc_modules',
        'params': [],
        'id': 1,
    },
)

modules = response.json()['result']
print('Namespaces:', sorted(modules.keys()))
print('Has trace:', 'trace' in modules)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "sort"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var modules map[string]string
    err = client.CallContext(context.Background(), &modules, "rpc_modules")
    if err != nil {
        log.Fatal(err)
    }

    names := make([]string, 0, len(modules))
    for name := range modules {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Printf("Namespaces: %v\n", names)
}
```

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Inspect the client software version string
- [`debug_traceTransaction`](https://www.dwellir.com/docs/avalanche/debug_traceTransaction) - Debug namespace example
- `trace_transaction` - Trace namespace example

---

## web3_clientVersion - Avalanche RPC Method

Returns the current client software version string for your Avalanche node, including the client name, version number, OS, and runtime.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

## When to Use This Method

`web3_clientVersion` is valuable for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Avalanche client:', clientVersion);

// Using fetch
const response = await fetch('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

client_version = w3.client_version
print(f'Avalanche client: {client_version}')

# web3_clientVersion - Avalanche RPC Method
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Avalanche client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/avalanche/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/avalanche/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/avalanche/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/avalanche/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Avalanche RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Avalanche.

> **Why Avalanche?** Build on the fastest smart contract platform with sub-second finality and customizable L1 subnets with sub-second finality, Evergreen subnets for institutions, and partnerships with Franklin Templeton, VanEck, and Bergen County.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for enterprise developers, RWA tokenizers, and teams building custom blockchain networks:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

# web3_sha3 - Avalanche RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/avalanche/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/avalanche/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/avalanche/web3_clientVersion) - Get node client version

---

## Base - Ethereum L2 Documentation

# Base - Ethereum L2 Documentation

## Why Build on Base?

Base is Coinbase's Ethereum Layer 2 solution, designed to bring the next billion users onchain. Built on Optimism's OP Stack, Base offers:

### **Lightning Fast Performance**

- **200ms Flashblocks** - Sub-block preconfirmations every \~200ms via Flashblocks
- **10-100x lower costs** than Ethereum mainnet
- **EIP-4844 enabled** - Leveraging blob data for even lower fees

### **Enterprise Security**

- **Backed by Coinbase** - Institutional-grade infrastructure
- **Ethereum security** - Inherits L1 security guarantees
- **Battle-tested** - Built on proven Optimism technology

### **Massive Ecosystem**

- **1M+ weekly active users** - Rapidly growing user base
- **$2B+ TVL** - Strong DeFi ecosystem
- **Major integrations** - Circle USDC, Chainlink, The Graph

## Quick Start with Base

Connect to Base in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Base mainnet
const provider = new JsonRpcProvider(
  'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Base mainnet
const web3 = new Web3(
  'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Base:', chainId === 8453);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';
import { base } from 'viem/chains';

// Create Base client
const client = createPublicClient({
  chain: base,
  transport: http('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

## Network Information

| Parameter    | Value    | Details      |
| ------------ | -------- | ------------ |
| Chain ID     | 8453     | Mainnet      |
| Block Time   | \~200ms  | Flashblocks  |
| Gas Token    | ETH      | Native token |
| RPC Standard | Ethereum | JSON-RPC 2.0 |

## API Reference

Base supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/) plus Base-specific Flashblocks methods and subscriptions.

## Flashblocks - 200ms Preconfirmations

Dwellir's Base endpoints support **Flashblocks**, delivering sub-block updates every \~200ms. Instead of waiting for a full 2-second block, your application can read preconfirmed state, stream pending transactions, and get receipts within milliseconds of submission.

### What You Get

| Capability                                                       | Description                                                                                                                                                   |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`"pending"` block tag**                                        | All state-reading methods (`eth_getBlockByNumber`, `eth_call`, `eth_getBalance`, `eth_getLogs`, etc.) accept `"pending"` to query the latest Flashblock state |
| **Preconfirmed receipts**                                        | `eth_getTransactionReceipt` returns receipts for transactions in the current Flashblock before the block is sealed                                            |
| **[`eth_sendRawTransactionSync`](https://www.dwellir.com/docs/eth_sendRawTransactionSync)** | Submit a transaction and get a full receipt synchronously in about 200ms                                                                                      |
| **[`eth_simulateV1`](https://www.dwellir.com/docs/eth_simulateV1)**                         | Simulate transaction bundles against the latest state with transfer tracing                                                                                   |
| **[`base_transactionStatus`](https://www.dwellir.com/docs/base_transactionStatus)**         | Check whether a transaction is still pending in the Base mempool                                                                                              |
| **WebSocket subscriptions**                                      | `newFlashblocks`, `newFlashblockTransactions`, and `pendingLogs` for real-time streaming                                                                      |

### Stream Flashblocks via WebSocket

newFlashblocks
newFlashblockTransactions
pendingLogs

```javascript
import WebSocket from 'ws';

const ws = new WebSocket('wss://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

ws.on('open', () => {
  // Stream full pending block state every ~200ms
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_subscribe',
    params: ['newFlashblocks'],
    id: 1,
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.params?.result) {
    const block = msg.params.result;
    console.log(`Flashblock #${parseInt(block.number, 16)} - ${block.transactions.length} txs`);
  }
});
```

```javascript
// Stream preconfirmed transaction hashes only
ws.send(JSON.stringify({
  jsonrpc: '2.0',
  method: 'eth_subscribe',
  params: ['newFlashblockTransactions'],
  id: 1,
}));

// Or pass true to get full transaction objects with logs
ws.send(JSON.stringify({
  jsonrpc: '2.0',
  method: 'eth_subscribe',
  params: ['newFlashblockTransactions', true],
  id: 2,
}));

ws.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.params?.result) {
    const tx = msg.params.result;
    if (typeof tx === 'string') {
      console.log('Tx hash:', tx);
    } else {
      console.log('Full tx:', tx.hash, '- logs:', tx.logs.length);
    }
  }
});
```

```javascript
// Stream logs from preconfirmed transactions
// Optionally filter by address and topics
ws.send(JSON.stringify({
  jsonrpc: '2.0',
  method: 'eth_subscribe',
  params: ['pendingLogs', {
    address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'],
  }],
  id: 1,
}));
```

### Query Preconfirmed State

Use the `"pending"` block tag to read state from the latest Flashblock:

```javascript
// Get balance reflecting the latest Flashblock
const balance = await provider.send('eth_getBalance', [address, 'pending']);

// Execute a call against Flashblock state
const result = await provider.send('eth_call', [{ to: contract, data: calldata }, 'pending']);

// Get logs from the current Flashblock
const logs = await provider.send('eth_getLogs', [{ fromBlock: 'pending', toBlock: 'pending' }]);
```

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // L2 specific: Check L1 data availability
  if (receipt.l1Fee) {
    console.log('L1 data cost:', receipt.l1Fee);
  }

  return receipt;
}
```

### Gas Optimization

Optimize gas costs on Base L2:

```javascript
// Estimate L2 execution gas
const l2Gas = await provider.estimateGas(tx);

// Get current L1 data fee (Base specific)
const l1DataFee = await provider.send('eth_estimateL1Fee', [tx]);

// Total cost = L2 execution + L1 data posting
const totalCost = l2Gas + BigInt(l1DataFee);
```

### Event Filtering

Efficiently query contract events:

```javascript
// Query events with automatic retry and pagination
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 2000; // Base recommended batch size

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);
  }

  return events;
}
```

---

## base_transactionStatus - Base RPC Method

# base_transactionStatus - Base RPC Method

Checks whether a transaction exists in the Base mempool. Returns `"Known"` if the transaction is pending in the mempool, or `"Unknown"` if it is not found (either already included in a block or never submitted).

This method is part of the `base_` namespace introduced with Flashblocks support.

Currently available on **Base Mainnet** only.

## Use Cases

- **Transaction tracking** — Check if a submitted transaction is still pending
- **Mempool monitoring** — Verify transaction propagation
- **Retry logic** — Determine whether to resubmit a transaction

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash to look up

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "base_transactionStatus",
  "params": ["0x..."],
  "id": 1
}
```

## Response Fields

- `status` (`String, required`): `"Known"` if the transaction is in the mempool, `"Unknown"` otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "Unknown"
  }
}
```

## Error Responses

### Invalid params

- Code: `-32602`
- Description: Invalid transaction hash format

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "base_transactionStatus",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
    "id": 1
  }'
```

```javascript
const response = await fetch(
  'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'base_transactionStatus',
      params: ['0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'],
      id: 1,
    }),
  }
);

const data = await response.json();
console.log('Transaction status:', data.result.status);
// "Known" = in mempool, "Unknown" = not in mempool
```

```python
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'base_transactionStatus',
        'params': ['0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'],
        'id': 1
    }
)

result = response.json()['result']
print(f"Status: {result['status']}")
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/base/eth_getTransactionByHash) — Get full transaction details
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) — Get transaction receipt
- [`eth_sendRawTransactionSync`](https://www.dwellir.com/docs/base/eth_sendRawTransactionSync) — Send transaction with synchronous confirmation

***

*Need help? Contact our [support team](mailto:support@dwellir.com) or check the [Base documentation](https://www.dwellir.com/docs/base).*

---

## debug_traceBlock - Base RPC Method

Traces all transactions in a block on Base by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Base - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Base
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Base RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Base:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Base blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/base/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/base/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/base/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/base/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Base RPC Method

Traces all transactions in a block on Base identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Base - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Base
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Base RPC Method
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Base:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Base:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/base/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/base/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/base/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/base/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/base/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Base RPC Method

Traces all transactions in a block on Base identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Base - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Base execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Base RPC Method
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Base:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Base:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/base/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/base/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/base/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/base/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Base RPC Method

Traces a call on Base without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Base - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Base
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x4200000000000000000000000000000000000006",
      "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Base RPC Method
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x4200000000000000000000000000000000000006",
        "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x4200000000000000000000000000000000000006",
        "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0x4200000000000000000000000000000000000006',
    data: '0x70a082310000000000000000000000004200000000000000000000000000000000000006'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0x4200000000000000000000000000000000000006',
    data: '0x70a082310000000000000000000000004200000000000000000000000000000000000006'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0x4200000000000000000000000000000000000006',
    'data': '0x70a082310000000000000000000000004200000000000000000000000000000000000006'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0x4200000000000000000000000000000000000006",
        "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Base to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Base:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Base:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0x4200000000000000000000000000000000000006',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/base/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/base/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/base/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Base RPC Method

Traces a transaction execution on Base by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Base
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'

# debug_traceTransaction - Base RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/base/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/base/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Base RPC Method

Returns a list of addresses owned by the client on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/base/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/base/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Base RPC Method

Returns the number of the most recent block on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_blockNumber` is fundamental for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Syncing Applications** - Keep your dApp in sync with the latest Base blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Base block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Base block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Base block: {block_number}')

# eth_blockNumber - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Base block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Base block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Base:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Base:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Base node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/base/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check if node is still syncing

---

## eth_call - Base RPC Method

Executes a new message call immediately without creating a transaction on Base. Used for reading smart contract state.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_call` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Base
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x4200000000000000000000000000000000000006';
const walletAddress = '0x4200000000000000000000000000000000000006';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Base. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0x4200000000000000000000000000000000000006';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0x4200000000000000000000000000000000000006';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Base
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Base

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x4200000000000000000000000000000000000006",
      "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Base RPC Method
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x4200000000000000000000000000000000000006",
      "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0x4200000000000000000000000000000000000006',
    '0x4200000000000000000000000000000000000006'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    data := common.FromHex("0x70a082310000000000000000000000004200000000000000000000000000000000000006")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Base RPC Method

Returns the chain ID used for transaction signing on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_chainId` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Base RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/base/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check sync status

---

## eth_coinbase - Base RPC Method

Checks the legacy `eth_coinbase` compatibility method on Base. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Base coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Base coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Base coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Base coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Base coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Base nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/base/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/base/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/base/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Base RPC Method

Estimates the gas necessary to execute a transaction on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Base
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0x4200000000000000000000000000000000000006', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0x4200000000000000000000000000000000000006', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Base.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Base may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0x4200000000000000000000000000000000000006',
  '0x4200000000000000000000000000000000000006',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0x4200000000000000000000000000000000000006",
    "to": "0x4200000000000000000000000000000000000006",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0x4200000000000000000000000000000000000006",
      "to": "0x4200000000000000000000000000000000000006",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Base RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0x4200000000000000000000000000000000000006', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/base/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Send transaction

---

## eth_estimateL1Fee - Estimate L1 data posting fee

# eth_estimateL1Fee - Estimate L1 data posting fee

Estimate L1 data posting fee on the Base network.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "method": "eth_estimateL1Fee",
        "params": [],
        "id": 1
      }'
```

## Response Fields

- `result` (`OBJECT, required`): The return value depends on the specific method being called.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Implementation Example

cURL
JavaScript

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateL1Fee",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_estimateL1Fee',
    params: [],
    id: 1
  })
});

const data = await response.json();
console.log(data.result);
```

---

## eth_feeHistory - Base RPC Method

Returns historical gas fee data on Base, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Base without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Base.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Base) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/base/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/base/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Base RPC Method

Returns the current gas price on Base in wei.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Base
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Base. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Base, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Base: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Base supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Base RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/base/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/base/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Base RPC Method

Returns the balance of a given address on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getBalance` is fundamental for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Base
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Base
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x4200000000000000000000000000000000000006",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Base. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Base`);
  return balance;
}

displayBalance('0x4200000000000000000000000000000000000006');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Base at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
address = '0x4200000000000000000000000000000000000006'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Base')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Base')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Base.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0x4200000000000000000000000000000000000006")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0x4200000000000000000000000000000000000006",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0x4200000000000000000000000000000000000006';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0x4200000000000000000000000000000000000006'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Base RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x4200000000000000000000000000000000000006")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/base/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/base/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Base RPC Method

Returns information about a block by hash on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getBlockByHash` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Base
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Base.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Base RPC Method
verify_block_still_canonical('0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Base.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Base RPC Method

Returns information about a block by block number on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getBlockByNumber` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Base
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Base with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Base. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Base RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/base/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/base/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Base RPC Method

# eth_getBlockReceipts - Base RPC Method

Returns all transaction receipts for a block on Base. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getBlockReceipts` is useful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Base, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Base. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xf47c4f664172629e9f74da58f558c6f553e2662adc7a3cf0af0292347bd2f358",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/base/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Base RPC Method

Returns the bytecode at a given address on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getCode` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Base
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Base:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Base:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x4200000000000000000000000000000000000006",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0x4200000000000000000000000000000000000006",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0x4200000000000000000000000000000000000006';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0x4200000000000000000000000000000000000006'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Base RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x4200000000000000000000000000000000000006")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/base/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/base/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Base RPC Method

Polls a filter on Base and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getFilterChanges` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Base
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x4200000000000000000000000000000000000006',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Base RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x4200000000000000000000000000000000000006'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Base:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/base/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/base/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/base/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/base/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/base/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Base RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Base. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getFilterLogs` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Base
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Base RPC Method
FILTER_ID=$(curl -s -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x4200000000000000000000000000000000000006"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0x4200000000000000000000000000000000000006',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0x4200000000000000000000000000000000000006'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/base/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/base/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/base/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Base RPC Method

# eth_getLogs - Base RPC Method

Returns an array of all logs matching a given filter object on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Base for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Base
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x4200000000000000000000000000000000000006';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0x4200000000000000000000000000000000000006';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Base.

```javascript
const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Base
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Base

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x4200000000000000000000000000000000000006",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0x4200000000000000000000000000000000000006",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0x4200000000000000000000000000000000000006",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0x4200000000000000000000000000000000000006',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0x4200000000000000000000000000000000000006',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/base/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Base RPC Method

Returns the value from a storage position at a given address on Base. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getStorageAt` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x4200000000000000000000000000000000000006",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x4200000000000000000000000000000000000006",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0x4200000000000000000000000000000000000006', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0x4200000000000000000000000000000000000006';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0x4200000000000000000000000000000000000006'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x4200000000000000000000000000000000000006")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Base:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0x4200000000000000000000000000000000000006';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Base:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/base/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/base/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Base RPC Method

# eth_getTransactionByHash - Base RPC Method

Returns the information about a transaction by transaction hash on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getTransactionByHash` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Base
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Base. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Base. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Base RPC Method

Returns the number of transactions sent from an address on Base, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getTransactionCount` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Base
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x4200000000000000000000000000000000000006",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0x4200000000000000000000000000000000000006",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0x4200000000000000000000000000000000000006', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Base nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0x4200000000000000000000000000000000000006';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0x4200000000000000000000000000000000000006'
nonce = get_transaction_count(address)
print(f'Base nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x4200000000000000000000000000000000000006")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Base nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Base:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0x4200000000000000000000000000000000000006');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/base/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/base/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Base RPC Method

# eth_getTransactionReceipt - Base RPC Method

Returns the receipt of a transaction by transaction hash on Base. Receipt is only available for mined transactions.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_getTransactionReceipt` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Base and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Base. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Base, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/base/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/base/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Base RPC Method

Returns the legacy `eth_hashrate` compatibility value on Base. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Base hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Base hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Base hashrate: {hashrate} H/s')

# eth_hashrate - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Base hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Base hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/base/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/base/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Base RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Base. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Base
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Base priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Base priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Base priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Base:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Base:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/base/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/base/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/base/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Base RPC Method

Checks the legacy `eth_mining` compatibility method on Base. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Base mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Base mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Base mining: {mining["result"]}')

# eth_mining - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Base mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Base mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/base/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/base/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Base RPC Method

Creates a filter on Base that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_newBlockFilter` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Block Monitoring** - Detect new blocks on Base as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Base RPC Method
FILTER_ID=$(curl -s -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Base:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/base/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/base/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Base RPC Method

Creates a filter object on Base based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_newFilter` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Event Monitoring** - Subscribe to specific contract events on Base such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x4200000000000000000000000000000000000006",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Base RPC Method
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0x4200000000000000000000000000000000000006",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x4200000000000000000000000000000000000006',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x4200000000000000000000000000000000000006',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Base:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0x4200000000000000000000000000000000000006',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/base/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/base/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/base/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/base/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Base RPC Method

Creates a filter on Base that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Mempool Monitoring** - Observe unconfirmed transactions on Base to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Base RPC Method
FILTER_ID=$(curl -s -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Base...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Base:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/base/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/base/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Base RPC Method

Returns the current Ethereum protocol version used by the Base node.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_protocolVersion` is useful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Base protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Base protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Base protocol version: {version}')

# eth_protocolVersion - Base RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Base protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Base protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Base nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/base/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/base/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/base/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Base RPC Method

Submits a pre-signed transaction for broadcast to Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Base
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0x4200000000000000000000000000000000000006', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Base mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/base/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Base RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0x4200000000000000000000000000000000000006")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/base/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendRawTransactionSync - Base RPC Method

# eth_sendRawTransactionSync - Base RPC Method

Submits a signed transaction and **waits synchronously** for inclusion in a Flashblock before returning. Unlike [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) which returns immediately with just the transaction hash, this method blocks until the transaction is included in a Flashblock and returns a full transaction receipt.

This enables sub-200ms transaction confirmation flows — the response includes the receipt with `blockNumber`, `gasUsed`, `logs`, `status`, and L2-specific fields like `l1Fee`.

## Use Cases

- **Instant confirmation UX** — Display transaction results immediately without polling
- **Synchronous workflows** — Chain dependent operations without receipt polling
- **Payment processing** — Confirm payment transactions inline
- **Bot and MEV operations** — Confirm execution before proceeding

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransactionSync",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `transactionHash` (`DATA, required`): 32-byte transaction hash
- `blockHash` (`DATA, required`): 32-byte block hash (may be zero for preconfirmed blocks)
- `blockNumber` (`QUANTITY, required`): Block number the transaction was included in
- `gasUsed` (`QUANTITY, required`): Gas consumed by the transaction
- `status` (`QUANTITY, required`): `0x1` for success, `0x0` for revert
- `logs` (`Array, required`): Logs emitted by the transaction
- `l1Fee` (`QUANTITY, required`): L1 data posting fee charged
- `l1GasUsed` (`QUANTITY, required`): Gas used for L1 data posting

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "transactionHash": "0x...",
    "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "blockNumber": "0x29442e3",
    "gasUsed": "0x5208",
    "status": "0x1",
    "logs": [],
    "l1Fee": "0xa5fce0e0",
    "l1GasUsed": "0x34dc"
  }
}
```

## Error Responses

### Failed to decode signed transaction

- Code: `-32602`
- Description: Invalid RLP-encoded transaction data

### Nonce too low

- Code: `-32000`
- Description: Transaction nonce already used

### Insufficient funds

- Code: `-32000`
- Description: Account has insufficient balance

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransactionSync",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { Wallet, parseEther, JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider(
  'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Build and sign the transaction
const tx = await wallet.populateTransaction({
  to: '0x...',
  value: parseEther('0.01'),
});
const signedTx = await wallet.signTransaction(tx);

// Send synchronously — returns full receipt, not just hash
const receipt = await provider.send('eth_sendRawTransactionSync', [signedTx]);

console.log('Status:', receipt.status === '0x1' ? 'success' : 'reverted');
console.log('Block:', parseInt(receipt.blockNumber, 16));
console.log('Gas used:', parseInt(receipt.gasUsed, 16));
console.log('L1 fee:', parseInt(receipt.l1Fee, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')
tx = {
    'nonce': w3.eth.get_transaction_count(account.address),
    'to': '0x...',
    'value': w3.to_wei(0.01, 'ether'),
    'gas': 21000,
    'maxFeePerGas': w3.eth.gas_price,
    'maxPriorityFeePerGas': w3.eth.max_priority_fee,
    'chainId': 8453
}

signed_tx = account.sign_transaction(tx)

# Send synchronously — returns full receipt
receipt = w3.provider.make_request(
    'eth_sendRawTransactionSync',
    [signed_tx.raw_transaction.hex()]
)

print(f"Status: {receipt['result']['status']}")
print(f"Block: {int(receipt['result']['blockNumber'], 16)}")
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) — Async version (returns hash immediately)
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/base/eth_getTransactionReceipt) — Get receipt for a previously sent transaction
- [`eth_simulateV1`](https://www.dwellir.com/docs/base/eth_simulateV1) — Simulate transactions before sending

***

*Need help? Contact our [support team](mailto:support@dwellir.com) or check the [Base documentation](https://www.dwellir.com/docs/base).*

---

## eth_sendTransaction - Base RPC Method

Creates and sends a new transaction from an unlocked account on Base. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Base RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Base tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Base tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Base tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Base tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Base tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Base dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/base/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Base RPC Method

Signs a transaction with the private key of the specified account on Base without submitting it to the network.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Base RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Base tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Base tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Base tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Base:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/base/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/base/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/base/eth_accounts) - List accounts available for signing

---

## eth_simulateV1 - Base RPC Method

# eth_simulateV1 - Base RPC Method

Simulates one or more transaction bundles against Base state, including Flashblocks preconfirmed state. Returns simulated block results with call status, gas used, return data, logs, and errors — without broadcasting anything onchain.

## Use Cases

- **Transaction preview** — Verify a swap or transfer will succeed before sending
- **Multi-call simulation** — Simulate a sequence of dependent transactions atomically
- **State override testing** — Override account balances or contract storage for what-if analysis
- **Gas estimation** — Get precise gas usage for complex call sequences

## Request Parameters

- `simulatePayload` (`Object, required`): Object containing `blockStateCalls`, `traceTransfers`, and `validation`
- `blockTag` (`QUANTITY|TAG, required`): Block number in hex, or `"latest"`, `"pending"`

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_simulateV1",
  "params": [
    {
      "blockStateCalls": [
        {
          "calls": [
            {
              "from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
              "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
              "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
            }
          ]
        }
      ],
      "traceTransfers": true,
      "validation": false
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Simulated block number
- `hash` (`DATA, required`): Simulated block hash
- `gasUsed` (`QUANTITY, required`): Total gas used
- `calls` (`Array, required`): Array of call results

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "number": "0x29442bb",
      "hash": "0xcefc6e6e...",
      "gasUsed": "0x79ce",
      "calls": [
        {
          "returnData": "0x00000000000000000000000000000000000000000000000000000000011420f9",
          "logs": [],
          "gasUsed": "0x79ce",
          "status": "0x1"
        }
      ]
    }
  ]
}
```

## Error Responses

### Invalid params

- Code: `-32602`
- Description: Malformed simulation payload

### Execution reverted

- Code: `-32000`
- Description: One or more simulated calls reverted

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_simulateV1",
    "params": [
      {
        "blockStateCalls": [
          {
            "calls": [
              {
                "from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
                "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                "data": "0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
              }
            ]
          }
        ],
        "traceTransfers": true,
        "validation": false
      },
      "latest"
    ],
    "id": 1
  }'
```

```javascript
const response = await fetch(
  'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'eth_simulateV1',
      params: [
        {
          blockStateCalls: [
            {
              calls: [
                {
                  from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
                  to: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
                  data: '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
                },
              ],
            },
          ],
          traceTransfers: true,
          validation: false,
        },
        'latest',
      ],
      id: 1,
    }),
  }
);

const data = await response.json();
for (const block of data.result) {
  for (const call of block.calls) {
    console.log('Status:', call.status === '0x1' ? 'success' : 'reverted');
    console.log('Gas used:', parseInt(call.gasUsed, 16));
    console.log('Return data:', call.returnData);
  }
}
```

```python
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_simulateV1',
        'params': [
            {
                'blockStateCalls': [
                    {
                        'calls': [
                            {
                                'from': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
                                'to': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
                                'data': '0x70a08231000000000000000000000000d8dA6BF26964aF9D7eEd9e03E53415D37aA96045'
                            }
                        ]
                    }
                ],
                'traceTransfers': True,
                'validation': False
            },
            'latest'
        ],
        'id': 1
    }
)

result = response.json()['result']
for block in result:
    for call in block['calls']:
        print(f"Status: {'success' if call['status'] == '0x1' else 'reverted'}")
        print(f"Gas used: {int(call['gasUsed'], 16)}")
        print(f"Return data: {call['returnData']}")
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) — Execute a single read-only call
- [`eth_estimateGas`](https://www.dwellir.com/docs/base/eth_estimateGas) — Estimate gas for a transaction
- [`eth_sendRawTransactionSync`](https://www.dwellir.com/docs/base/eth_sendRawTransactionSync) — Send a transaction and wait for Flashblock inclusion

***

*Need help? Contact our [support team](mailto:support@dwellir.com) or check the [Base documentation](https://www.dwellir.com/docs/base).*

---

## eth_syncing - Base RPC Method

# eth_syncing - Base RPC Method

Returns the sync status of your Base node - either `false` when fully synced, or an object describing the sync progress.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_syncing` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Base node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Base node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Base node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/base/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/base/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/base/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/base/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Base RPC Method

Removes a filter on Base that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`eth_uninstallFilter` is important for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Base RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/base/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/base/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/base/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/base/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/base/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Base RPC Method

Checks whether the connected Base client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`net_listening` is useful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Base node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Base node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Base RPC Method
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Base node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/base/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/base/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/base/web3_clientVersion) - Get node client info

---

## net_peerCount - Base RPC Method

Returns the number of peers currently connected to your Base node.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`net_peerCount` is important for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Base deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Base peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Base peers: {peer_count}')

# net_peerCount - Base RPC Method
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Base peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Base nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/base/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/base/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/base/web3_clientVersion) - Get node client info

---

## net_version - Base RPC Method

Returns the current network ID on Base as a decimal string. The network ID identifies which network the node is connected to.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`net_version` is essential for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Endpoint Identification** - Confirm your application is connected to the expected Base network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Base network ID:', networkId);

// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Base network ID: {network_id}')

# net_version - Base RPC Method
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Base network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/base/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/base/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/base/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check node sync progress

---

## rollup_gasPrices - Get L2 gas price oracle data

# rollup_gasPrices - Get L2 gas price oracle data

Get L2 gas price oracle data on the Base network.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "method": "rollup_gasPrices",
        "params": [],
        "id": 1
      }'
```

## Response Fields

- `result` (`OBJECT, required`): The return value depends on the specific method being called.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Implementation Example

cURL
JavaScript

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rollup_gasPrices",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rollup_gasPrices',
    params: [],
    id: 1
  })
});

const data = await response.json();
console.log(data.result);
```

---

## rollup_getInfo - Get rollup configuration

# rollup_getInfo - Get rollup configuration

Get rollup configuration on the Base network.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "method": "rollup_getInfo",
        "params": [],
        "id": 1
      }'
```

## Response Fields

- `result` (`OBJECT, required`): The return value depends on the specific method being called.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Implementation Example

cURL
JavaScript

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rollup_getInfo",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rollup_getInfo',
    params: [],
    id: 1
  })
});

const data = await response.json();
console.log(data.result);
```

---

## trace_block - Base RPC Method

# trace_block - Base RPC Method

Returns traces for all transactions in a block on Base.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace all transactions in a block by block number** - Get the full trace of every transaction in a block on Base
- **Block-level execution analysis** - Inspect all internal calls, transfers, and contract interactions within a block for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **MEV research** - Analyze transaction ordering, sandwich patterns, and arbitrage across a full block
- **Historical block replay** - Replay and trace blocks at any point in Base chain history

## Best Practices

- Similar to trace\_replayBlockTransactions but without explicit replay configuration
- Use block hash variant (`debug_traceBlockByHash`) for reorg-safe queries
- Traces from dense blocks can be very large; process results in batches
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex or tag (latest, earliest, pending)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_block",
  "params": ["latest"],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_block",
    "params": ["latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_block', ['latest']);
console.log('Traces in block:', traces.length);
for (const trace of traces.slice(0, 5)) {
  console.log(`  ${trace.action.from} -> ${trace.action.to} (${trace.type})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_block', ['latest'])
for trace in traces['result'][:5]:
    action = trace['action']
    print(f'{action["from"]} -> {action.get("to", "CREATE")} ({trace["type"]})')
```

## Related Methods

- [`trace_filter`](https://www.dwellir.com/docs/base/trace_filter) - Filter traces by address or block range
- [`trace_transaction`](https://www.dwellir.com/docs/base/trace_transaction) - Trace a specific transaction
- [`trace_get`](https://www.dwellir.com/docs/base/trace_get) - Get a specific trace by index

---

## trace_call - Base RPC Method

# trace_call - Base RPC Method

Traces a call without creating a transaction on Base, returning the trace output.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace simulated execution of multiple calls** - Preview internal calls before committing a transaction on Base
- **Pre-execution analysis** - Test contract interactions without spending gas for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Batch trace simulations** - Execute several calls sequentially where each can depend on prior state changes
- **Contract interaction analysis** - Understand how multiple contracts interact through a simulated execution chain

## Best Practices

- Similar to debug\_traceCall but supports multiple calls in one request
- Each call has its own trace configuration for fine-grained control
- Use for batch simulation analysis of dependent call sequences
- Requires archive node access for historical block tracing

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (from, to, gas, value, data)
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations
- `blockNumber` (`QUANTITY|TAG, optional`): Block number or tag (default: latest)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_call",
  "params": [
    {
      "to": "0x4200000000000000000000000000000000000006",
      "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"
    },
    ["trace"],
    "latest"
  ],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_call",
    "params": [
      {"to": "0x4200000000000000000000000000000000000006", "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"},
      ["trace"],
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const result = await provider.send('trace_call', [
  {
    to: '0x4200000000000000000000000000000000000006',
    data: '0x70a082310000000000000000000000004200000000000000000000000000000000000006'
  },
  ['trace'],
  'latest'
]);
console.log('Trace output:', result.trace);
console.log('VM trace:', result.vmTrace);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

result = w3.provider.make_request('trace_call', [
    {
        'to': '0x4200000000000000000000000000000000000006',
        'data': '0x70a082310000000000000000000000004200000000000000000000000000000000000006'
    },
    ['trace'],
    'latest'
])
print(f'Trace: {result["result"]["trace"]}')
```

## Related Methods

- [`trace_filter`](https://www.dwellir.com/docs/base/trace_filter) - Filter traces by criteria
- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) - Execute call without trace
- [`trace_transaction`](https://www.dwellir.com/docs/base/trace_transaction) - Trace a specific transaction

---

## trace_callMany - Base RPC Method

# trace_callMany - Base RPC Method

Traces multiple calls in sequence on Base, where each call can depend on the state changes of the previous one.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace multiple calls across different blocks** - Execute calls at different historical block heights for cross-block state analysis on Base
- **Historical state comparison** - Compare how the same call would execute at different points in Base chain history
- **Multi-step simulation** - Simulate a sequence of dependent calls where each step builds on the previous one for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Batch operation tracing** - Preview multiple contract interactions in a single RPC request

## Best Practices

- Each call specifies its own block number for cross-block analysis
- Useful for comparing state across time without multiple requests
- More efficient than separate trace\_call requests for multi-step workflows
- Requires archive node access for historical block state

## Request Parameters

- `calls` (`Array, required`): Array of [callObject, traceTypes] pairs
- `blockNumber` (`QUANTITY|TAG, optional`): Block number or tag (default: latest)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_callMany",
  "params": [
    [
      [{"to": "0x4200000000000000000000000000000000000006", "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"}, ["trace"]],
      [{"to": "0x4200000000000000000000000000000000000006", "data": "0x18160ddd"}, ["trace"]]
    ],
    "latest"
  ],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_callMany",
    "params": [
      [
        [{"to": "0x4200000000000000000000000000000000000006", "data": "0x70a082310000000000000000000000004200000000000000000000000000000000000006"}, ["trace"]],
        [{"to": "0x4200000000000000000000000000000000000006", "data": "0x18160ddd"}, ["trace"]]
      ],
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const results = await provider.send('trace_callMany', [
  [
    [{ to: '0x4200000000000000000000000000000000000006', data: '0x70a082310000000000000000000000004200000000000000000000000000000000000006' }, ['trace']],
    [{ to: '0x4200000000000000000000000000000000000006', data: '0x18160ddd' }, ['trace']]
  ],
  'latest'
]);
console.log('Call results:', results.length);
for (const result of results) {
  console.log('  Output:', result.output);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

results = w3.provider.make_request('trace_callMany', [
    [
        [{'to': '0x4200000000000000000000000000000000000006', 'data': '0x70a082310000000000000000000000004200000000000000000000000000000000000006'}, ['trace']],
        [{'to': '0x4200000000000000000000000000000000000006', 'data': '0x18160ddd'}, ['trace']]
    ],
    'latest'
])
for i, result in enumerate(results['result']):
    print(f'Call {i}: output={result.get("output", "N/A")}')
```

## Related Methods

- [`trace_call`](https://www.dwellir.com/docs/base/trace_call) - Trace a single call
- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) - Execute call without trace

---

## trace_filter - Base RPC Method

# trace_filter - Base RPC Method

Returns traces matching a filter on Base.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Search for traces matching address criteria** - Find all internal transactions involving a specific address on Base
- **Find all interactions with a contract** - Track every call, delegate call, and create operation targeting a contract for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Audit contract usage patterns** - Analyze how contracts are used over time across block ranges
- **Forensic analysis** - Investigate transaction patterns and address-level activity across the chain

## Best Practices

- Very resource-intensive on large block ranges; narrow the range as much as possible
- Use specific fromAddress or toAddress filters to reduce response size
- Limit block range to small chunks and paginate results
- Many provider-managed endpoints disable this method due to computational cost

## Request Parameters

- `filterObject` (`Object, required`): Filter criteria (see below)
- `fromBlock` (`QUANTITY|TAG, required`): Start block (hex or tag)
- `toBlock` (`QUANTITY|TAG, required`): End block (hex or tag)
- `fromAddress` (`Array<DATA>, required`): Filter by sender addresses
- `toAddress` (`Array<DATA>, required`): Filter by receiver addresses
- `after` (`QUANTITY, required`): Offset for pagination
- `count` (`QUANTITY, required`): Max results to return

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_filter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "toAddress": ["0x4200000000000000000000000000000000000006"],
    "count": 10
  }],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_filter",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "toAddress": ["0x4200000000000000000000000000000000000006"],
      "count": 10
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_filter', [{
  fromBlock: 'latest',
  toBlock: 'latest',
  toAddress: ['0x4200000000000000000000000000000000000006'],
  count: 10
}]);
console.log('Matching traces:', traces.length);
for (const trace of traces) {
  console.log(`  Block ${trace.blockNumber}: ${trace.action.from} -> ${trace.action.to}`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_filter', [{
    'fromBlock': 'latest',
    'toBlock': 'latest',
    'toAddress': ['0x4200000000000000000000000000000000000006'],
    'count': 10
}])
for trace in traces['result']:
    action = trace['action']
    print(f'Block {trace["blockNumber"]}: {action["from"]} -> {action.get("to", "CREATE")}')
```

## Related Methods

- [`trace_block`](https://www.dwellir.com/docs/base/trace_block) - Get all traces in a block
- [`trace_transaction`](https://www.dwellir.com/docs/base/trace_transaction) - Get traces for a specific transaction
- [`eth_getLogs`](https://www.dwellir.com/docs/base/eth_getLogs) - Filter event logs

---

## trace_get - Base RPC Method

# trace_get - Base RPC Method

Returns a trace at a specific position within a transaction on Base.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Get a specific trace by transaction hash and trace index** - Retrieve individual trace entries from a transaction on Base
- **Pinpoint specific internal calls** - Isolate a particular sub-call at a known position in the call tree for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Targeted debugging** - Investigate a specific call depth without retrieving the full transaction trace
- **Combine with trace\_transaction** - Discover trace indices from the full trace, then fetch details individually

## Best Practices

- Requires knowing the trace address and index in advance
- Combine with trace\_transaction to discover trace indices first
- Supports fetching multiple traces by passing an array of indices
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `indices` (`Array<QUANTITY>, required`): Trace index positions (e.g., ["0x0"] for the first trace)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_get",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", ["0x0"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_get",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", ["0x0"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const trace = await provider.send('trace_get', [
  '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269',
  ['0x0']
]);
console.log('Trace type:', trace.type);
console.log('From:', trace.action.from);
console.log('To:', trace.action.to);
console.log('Value:', trace.action.value);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('trace_get', [
    '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269',
    ['0x0']
])
result = trace['result']
print(f'Type: {result["type"]}')
print(f'From: {result["action"]["from"]}')
print(f'To: {result["action"]["to"]}')
```

## Related Methods

- [`trace_transaction`](https://www.dwellir.com/docs/base/trace_transaction) - Get all traces for a transaction
- [`trace_block`](https://www.dwellir.com/docs/base/trace_block) - Get all traces in a block

---

## trace_replayBlockTransactions - Base RPC Method

# trace_replayBlockTransactions - Base RPC Method

Replays all transactions in a block on Base and returns the requested traces.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Replay all transactions in a block** - Get vmTrace, stateDiff, and trace for every transaction in a block on Base
- **Comprehensive block-level execution analysis** - Audit exactly how each transaction in a block modified state for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Audit entire block execution** - Verify that all transactions in a block executed as expected
- **Historical block replay** - Re-execute blocks at any point in Base chain history

## Best Practices

- Very resource-intensive; each transaction is fully traced with all requested types
- Limit to small blocks or use specific tracer types to reduce response size
- Request only the trace types you actually need to minimize overhead
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex or tag
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_replayBlockTransactions",
  "params": ["latest", ["trace"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_replayBlockTransactions",
    "params": ["latest", ["trace"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const results = await provider.send('trace_replayBlockTransactions', [
  'latest',
  ['trace']
]);
console.log('Transactions replayed:', results.length);
for (const result of results.slice(0, 3)) {
  console.log(`  Tx ${result.transactionHash}: ${result.trace.length} traces`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

results = w3.provider.make_request('trace_replayBlockTransactions', [
    'latest',
    ['trace']
])
for tx in results['result'][:3]:
    print(f'Tx {tx["transactionHash"]}: {len(tx["trace"])} traces')
```

## Related Methods

- [`trace_replayTransaction`](https://www.dwellir.com/docs/base/trace_replayTransaction) - Replay a single transaction
- [`trace_block`](https://www.dwellir.com/docs/base/trace_block) - Get traces without replay
- [`trace_filter`](https://www.dwellir.com/docs/base/trace_filter) - Filter traces by criteria

---

## trace_replayTransaction - Base RPC Method

# trace_replayTransaction - Base RPC Method

Replays a transaction on Base and returns the requested traces.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Replay and trace a transaction execution** - Get vmTrace, stateDiff, and trace in a single call for comprehensive analysis on Base
- **State diff extraction** - See exact account balance, nonce, code, and storage changes caused by a transaction for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **VM trace debugging** - Get opcode-level execution details alongside the structured call trace
- **Comprehensive transaction analysis** - Combine all three trace types in one request for complete execution visibility

## Best Practices

- Returns more detailed trace data than debug\_traceTransaction
- Combine vmTrace with trace array for a full picture of opcode and call-level execution
- Request all three trace types (trace, vmTrace, stateDiff) for maximum detail
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_replayTransaction",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", ["trace"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_replayTransaction",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269", ["trace"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const result = await provider.send('trace_replayTransaction', [
  '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269',
  ['trace']
]);
console.log('Trace:', result.trace.length, 'entries');
console.log('State diff:', result.stateDiff ? 'present' : 'not requested');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

result = w3.provider.make_request('trace_replayTransaction', [
    '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269',
    ['trace']
])
trace = result['result']
print(f'Trace entries: {len(trace["trace"])}')
```

## Related Methods

- [`trace_replayBlockTransactions`](https://www.dwellir.com/docs/base/trace_replayBlockTransactions) - Replay all transactions in a block
- [`trace_transaction`](https://www.dwellir.com/docs/base/trace_transaction) - Get traces without replay
- [`trace_block`](https://www.dwellir.com/docs/base/trace_block) - Get all traces in a block

---

## trace_transaction - Base RPC Method

# trace_transaction - Base RPC Method

Returns all traces for a specific transaction on Base.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Get parity-style transaction trace** - Retrieve the full trace of all internal calls, state changes, and value transfers for consumer dApps, SocialFi, NFT marketplaces, and merchant payment integrations
- **Analyze internal calls and state changes** - See every sub-call, delegate call, and contract creation triggered by a transaction on Base
- **Audit transaction execution paths** - Follow the exact flow of execution through contracts to verify correctness
- **Track value flows** - Trace how funds move through multiple contracts in a single transaction

## Best Practices

- Parity-style traces are more detailed than the debug namespace equivalent
- Use trace\_replayTransaction for combined trace, vmTrace, and stateDiff output
- Results include both stateDiff and vmTrace sections for comprehensive analysis
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_transaction",
  "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_transaction",
    "params": ["0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_transaction', [
  '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'
]);
console.log('Traces:', traces.length);
for (const trace of traces) {
  console.log(`  ${trace.action.from} -> ${trace.action.to} (${trace.type})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_transaction', [
    '0x5c884a466fb59ee69114a0c99cb15d4d8af670a37be53fd59ffda3b5566b4269'
])
for trace in traces['result']:
    action = trace['action']
    print(f'{action["from"]} -> {action.get("to", "CREATE")} ({trace["type"]})')
```

## Related Methods

- [`trace_get`](https://www.dwellir.com/docs/base/trace_get) - Get a specific trace by index
- [`trace_block`](https://www.dwellir.com/docs/base/trace_block) - Get all traces in a block
- [`trace_filter`](https://www.dwellir.com/docs/base/trace_filter) - Filter traces by criteria

---

## web3_clientVersion - Base RPC Method

Returns the current client software version string for your Base node, including the client name, version number, OS, and runtime.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

## When to Use This Method

`web3_clientVersion` is valuable for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Base client:', clientVersion);

// Using fetch
const response = await fetch('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Base client: {client_version}')

# web3_clientVersion - Base RPC Method
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Base client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/base/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/base/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/base/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/base/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Base RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Base.

> **Why Base?** Build on Coinbase's L2 with 54% of L2 market revenue and direct access to 110M+ Coinbase users with $8B+ TVL, $0.08 gas fees, built-in Coinbase distribution, and seamless fiat rails.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for consumer app developers, SocialFi builders, and teams seeking easy fiat onramps:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Base RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/base/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/base/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/base/web3_clientVersion) - Get node client version

---

## Berachain RPC with Dwellir

## Why Build on Berachain?

### High‑Performance EVM

- Built on a modular EVM (Polaris) for efficient execution and developer‑friendly precompiles
- Fast finality via CometBFT (BFT consensus), ideal for low‑latency reads and responsive UX
- EVM compatibility out of the box. Keep using Solidity, Hardhat, Foundry, viem, and ethers.js

### Proof‑of‑Liquidity Alignment

- Separation of gas and governance unlocks healthier incentive design
- Liquidity provision powers governance emissions (BGT) to align validators, protocols, and users
- Ecosystem‑driven rewards encourage deep liquidity for DeFi building blocks

### Interoperability & Modularity

- Cosmos SDK foundation with IBC‑friendly architecture for cross‑chain connectivity
- Polaris EVM’s modular design enables stateful precompiles and chain‑specific extensions without breaking EVM apps
- Drop‑in migration: reuse your contracts, tooling, and workflows

## Quick Start with Berachain

Connect to Berachain in seconds with Dwellir's optimized endpoints:

### Installation & Setup

cURL
Ethers.js v6
Viem
Python (web3.py)

```bash
# Berachain RPC with Dwellir
curl -s -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# Verify chain id
curl -s -X POST https://api-berachain-bepolia.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

```ts
import { JsonRpcProvider } from 'ethers';

// Mainnet
const mainnet = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
console.log(await mainnet.getBlockNumber());
console.log((await mainnet.getNetwork()).chainId); // 80094

// Bepolia testnet
const bepolia = new JsonRpcProvider('https://api-berachain-bepolia.n.dwellir.com/YOUR_API_KEY');
console.log(await bepolia.getBlockNumber());
console.log((await bepolia.getNetwork()).chainId); // 80069
```

```ts
import { createPublicClient, http, parseEther } from 'viem';

const mainnet = createPublicClient({
  transport: http('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'),
});

const blockNumber = await mainnet.getBlockNumber();
const balance = await mainnet.getBalance({ address: '0x0000000000000000000000000000000000000000' });
```

```py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
assert w3.is_connected()
print(w3.eth.chain_id)      # 80094
print(w3.eth.block_number)
```

## Network Information

| Parameter        | Value                 | Details           |
| ---------------- | --------------------- | ----------------- |
| Mainnet Chain ID | 80094 (0x138de)       | Berachain Mainnet |
| Testnet Chain ID | 80069 (0x138c5)       | Bepolia Testnet   |
| RPC Standard     | Ethereum JSON-RPC 2.0 | EVM-compatible    |

## API Reference

Berachain supports the full [Ethereum JSON-RPC API](https://ethereum.org/developers/docs/apis/json-rpc/).

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
async function waitForConfirmations(provider, txHash, confirmations = 1) {
  const receipt = await provider.waitForTransaction(txHash, confirmations);
  return receipt;
}
```

### Gas Optimization

Use EIP‑1559 dynamic fees and estimate execution gas:

```javascript
const feeData = await provider.getFeeData();
const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: await provider.estimateGas({ to: recipient, value: amount }),
};
```

### Event Filtering

Query events in bounded ranges to avoid overfetching:

```javascript
async function getEvents(contract, filter, fromBlock, toBlock, batchSize = 2000) {
  const events = [];
  for (let i = fromBlock; i <= toBlock; i += batchSize) {
    const batch = await contract.queryFilter(filter, i, Math.min(i + batchSize - 1, toBlock));
    events.push(...batch);
  }
  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine independent calls in a single POST to reduce round‑trips:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const res = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});
const results = await res.json();
```

### 2. **Connection Pooling**

Reuse provider/clients instead of recreating per call:

```ts
import { JsonRpcProvider } from 'ethers';

class BeraProvider {
  static instance: JsonRpcProvider | null = null;
  static get() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data (e.g., past blocks, ABIs) and debounce hot paths:

```ts
const cache = new Map<string, unknown>();
async function getBlockCached(n: number) {
  const k = `block_${n}`;
  if (!cache.has(k)) {
    cache.set(k, await BeraProvider.get().getBlock(n));
  }
  return cache.get(k);
}
```

## Migration Guide

Moving from Ethereum or another EVM chain typically requires:

- Update RPC URL to the Berachain mainnet endpoint: `https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY`
- Verify chain ID via `eth_chainId` (0x138de mainnet) before sending transactions
- Re-check gas settings (EIP‑1559) and any hard‑coded addresses

```ts
// Before
const provider = new JsonRpcProvider('https://eth.example');

// After (Berachain mainnet)
const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
```

## Resources & Tools

- [Berachain Docs](https://docs.berachain.com/)
- [Dwellir Dashboard](https://dashboard.dwellir.com)
- [Dwellir Support](mailto:support@dwellir.com)

### Related Reading

- [Top 7 Berachain RPC Providers 2026](https://www.dwellir.com/blog/top-berachain-rpc-providers)

## Troubleshooting Common Issues

- Wrong chain: ensure `eth_chainId` returns `0x138de` (mainnet) or `0x138c5` (Bepolia).
- Hex quantities: send and parse `0x`-prefixed hex strings (no leading zeros).
- Timeouts/rate limits: implement retries with exponential backoff on HTTP 429 or -32005.

## FAQs

- Do I need an API key? Yes, append `/YOUR_API_KEY` to all endpoints.
- WebSockets? Use HTTP endpoints above; WebSocket availability may vary.
- Explorers/faucets? N/A.

## Smoke Tests

- curl: `eth_blockNumber` to both endpoints returns `result: "0x..."`.
- ethers v6: `getBlockNumber()` resolves; `getNetwork().chainId` equals 80094 or 80069.
- web3.py: `is_connected()` is True; `chain_id` and `block_number` query succeed.

***

Start building on Berachain with Dwellir’s reliable RPC.

---

## debug_traceBlock - Berachain RPC Method

Traces all transactions in a block on Berachain by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Berachain - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Berachain
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Berachain RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Berachain:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Berachain blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/berachain/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/berachain/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/berachain/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/berachain/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Berachain RPC Method

Traces all transactions in a block on Berachain identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Berachain - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Berachain
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Berachain RPC Method
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Berachain:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Berachain:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/berachain/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/berachain/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/berachain/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/berachain/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/berachain/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Berachain RPC Method

Traces all transactions in a block on Berachain identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Berachain - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Berachain execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Berachain RPC Method
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Berachain:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Berachain:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/berachain/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/berachain/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/berachain/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/berachain/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Berachain RPC Method

Traces a call on Berachain without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Berachain - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Berachain
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Berachain RPC Method
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
        "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
        "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    data: '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    data: '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    'data': '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
        "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Berachain to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Berachain:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Berachain:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/berachain/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/berachain/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/berachain/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/berachain/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Berachain RPC Method

Traces a transaction execution on Berachain by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Berachain
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671'

# debug_traceTransaction - Berachain RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/berachain/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/berachain/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Berachain RPC Method

Returns a list of addresses owned by the client on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/berachain/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/berachain/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Berachain RPC Method

Returns the number of the most recent block on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_blockNumber` is fundamental for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Syncing Applications** - Keep your dApp in sync with the latest Berachain blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Berachain block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Berachain block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Berachain block: {block_number}')

# eth_blockNumber - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Berachain block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Berachain block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Berachain:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Berachain:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Berachain node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/berachain/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check if node is still syncing

---

## eth_call - Berachain RPC Method

Executes a new message call immediately without creating a transaction on Berachain. Used for reading smart contract state.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_call` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Berachain
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';
const walletAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Berachain. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Berachain
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Berachain

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Berachain RPC Method
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    data := common.FromHex("0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Berachain RPC Method

Returns the chain ID used for transaction signing on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_chainId` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Berachain RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/berachain/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check sync status

---

## eth_coinbase - Berachain RPC Method

Checks the legacy `eth_coinbase` compatibility method on Berachain. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Berachain coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Berachain coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Berachain coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Berachain coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Berachain coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Berachain nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/berachain/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/berachain/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/berachain/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Berachain RPC Method

Estimates the gas necessary to execute a transaction on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Berachain
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Berachain.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Berachain may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Berachain RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/berachain/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Berachain RPC Method

Returns historical gas fee data on Berachain, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Berachain without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Berachain.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Berachain) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/berachain/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/berachain/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Berachain RPC Method

Returns the current gas price on Berachain in wei.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Berachain
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Berachain. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Berachain, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Berachain: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Berachain supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Berachain RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/berachain/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/berachain/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Berachain RPC Method

Returns the balance of a given address on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getBalance` is fundamental for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Berachain
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Berachain
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Berachain. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Berachain`);
  return balance;
}

displayBalance('0x7507c1dc16935B82698e4C63f2746A2fCf994dF8');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Berachain at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Berachain')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Berachain')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Berachain.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Berachain RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/berachain/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/berachain/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Berachain RPC Method

Returns information about a block by hash on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getBlockByHash` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Berachain
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Berachain.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Berachain RPC Method
verify_block_still_canonical('0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Berachain.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Berachain RPC Method

Returns information about a block by block number on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getBlockByNumber` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Berachain
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Berachain with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Berachain. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Berachain RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/berachain/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/berachain/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Berachain RPC Method

# eth_getBlockReceipts - Berachain RPC Method

Returns all transaction receipts for a block on Berachain. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getBlockReceipts` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Berachain, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Berachain. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x969b796c2c98668e687636319dba231c7f3f77e1c89dab82fba3d17f8f0f9e55",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/berachain/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/berachain/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Berachain RPC Method

Returns the bytecode at a given address on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getCode` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Berachain
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Berachain:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Berachain:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Berachain RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/berachain/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/berachain/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Berachain RPC Method

Polls a filter on Berachain and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getFilterChanges` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Berachain
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Berachain RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Berachain:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/berachain/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/berachain/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/berachain/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/berachain/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/berachain/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Berachain RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Berachain. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getFilterLogs` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Berachain
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Berachain RPC Method
FILTER_ID=$(curl -s -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/berachain/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/berachain/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/berachain/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Berachain RPC Method

# eth_getLogs - Berachain RPC Method

Returns an array of all logs matching a given filter object on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Berachain for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Berachain
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Berachain.

```javascript
const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Berachain
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Berachain

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/berachain/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Berachain RPC Method

Returns the value from a storage position at a given address on Berachain. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getStorageAt` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Berachain:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Berachain:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/berachain/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/berachain/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/berachain/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Berachain RPC Method

# eth_getTransactionByHash - Berachain RPC Method

Returns the information about a transaction by transaction hash on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getTransactionByHash` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Berachain
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Berachain. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Berachain. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/berachain/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Berachain RPC Method

Returns the number of transactions sent from an address on Berachain, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getTransactionCount` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Berachain
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Berachain nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'
nonce = get_transaction_count(address)
print(f'Berachain nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Berachain nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Berachain:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/berachain/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/berachain/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/berachain/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Berachain RPC Method

# eth_getTransactionReceipt - Berachain RPC Method

Returns the receipt of a transaction by transaction hash on Berachain. Receipt is only available for mined transactions.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_getTransactionReceipt` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Berachain and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Berachain. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Berachain, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/berachain/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/berachain/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Berachain RPC Method

Returns the legacy `eth_hashrate` compatibility value on Berachain. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Berachain hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Berachain hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Berachain hashrate: {hashrate} H/s')

# eth_hashrate - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Berachain hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Berachain hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/berachain/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/berachain/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Berachain RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Berachain. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Berachain
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Berachain priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Berachain priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Berachain priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Berachain:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Berachain:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/berachain/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/berachain/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/berachain/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Berachain RPC Method

Checks the legacy `eth_mining` compatibility method on Berachain. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Berachain mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Berachain mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Berachain mining: {mining["result"]}')

# eth_mining - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Berachain mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Berachain mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/berachain/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/berachain/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Berachain RPC Method

Creates a filter on Berachain that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_newBlockFilter` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Block Monitoring** - Detect new blocks on Berachain as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Berachain RPC Method
FILTER_ID=$(curl -s -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Berachain:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/berachain/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/berachain/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Berachain RPC Method

Creates a filter object on Berachain based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_newFilter` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Event Monitoring** - Subscribe to specific contract events on Berachain such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Berachain RPC Method
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Berachain:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/berachain/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/berachain/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/berachain/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/berachain/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Berachain RPC Method

Creates a filter on Berachain that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Mempool Monitoring** - Observe unconfirmed transactions on Berachain to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Berachain RPC Method
FILTER_ID=$(curl -s -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Berachain...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Berachain:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/berachain/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/berachain/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Berachain RPC Method

Returns the current Ethereum protocol version used by the Berachain node.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_protocolVersion` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Berachain protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Berachain protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Berachain protocol version: {version}')

# eth_protocolVersion - Berachain RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Berachain protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Berachain protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Berachain nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/berachain/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/berachain/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Berachain RPC Method

Submits a pre-signed transaction for broadcast to Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Berachain
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Berachain mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/berachain/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Berachain RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0x7507c1dc16935B82698e4C63f2746A2fCf994dF8")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/berachain/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/berachain/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Berachain RPC Method

Creates and sends a new transaction from an unlocked account on Berachain. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Berachain RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Berachain tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Berachain tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Berachain tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Berachain tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Berachain tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Berachain dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/berachain/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/berachain/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Berachain RPC Method

Signs a transaction with the private key of the specified account on Berachain without submitting it to the network.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Berachain RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Berachain tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Berachain tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Berachain tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Berachain:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/berachain/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/berachain/eth_accounts) - List accounts available for signing

---

## eth_syncing - Berachain RPC Method

# eth_syncing - Berachain RPC Method

Returns the sync status of your Berachain node - either `false` when fully synced, or an object describing the sync progress.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_syncing` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Berachain node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Berachain node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Berachain node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/berachain/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/berachain/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Berachain RPC Method

Removes a filter on Berachain that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`eth_uninstallFilter` is important for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Berachain RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/berachain/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/berachain/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/berachain/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/berachain/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/berachain/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Berachain RPC Method

Checks whether the connected Berachain client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`net_listening` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Berachain node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Berachain node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Berachain RPC Method
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Berachain node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/berachain/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/berachain/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Get node client info

---

## net_peerCount - Berachain RPC Method

Returns the number of peers currently connected to your Berachain node.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`net_peerCount` is important for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Berachain deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Berachain peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Berachain peers: {peer_count}')

# net_peerCount - Berachain RPC Method
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Berachain peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Berachain nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/berachain/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/berachain/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Get node client info

---

## net_version - Berachain RPC Method

Returns the current network ID on Berachain as a decimal string. The network ID identifies which network the node is connected to.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`net_version` is essential for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Endpoint Identification** - Confirm your application is connected to the expected Berachain network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Berachain network ID:', networkId);

// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Berachain network ID: {network_id}')

# net_version - Berachain RPC Method
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Berachain network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/berachain/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/berachain/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/berachain/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check node sync progress

---

## rpc_modules - Berachain RPC Method

# rpc_modules - Berachain RPC Method

Returns the enabled JSON-RPC namespaces exposed by the connected Berachain endpoint together with their version strings.

> **Non-standard method.** `rpc_modules` is a client-introspection RPC that is commonly available on Geth-compatible stacks, but it is not part of the core Ethereum Execution API method set. Availability varies by client and operator policy.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`rpc_modules` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Capability Discovery** - Detect whether namespaces like `debug`, `trace`, `txpool`, or `erigon` are exposed before attempting those calls
- **Client Diagnostics** - Verify what the serving node has enabled when debugging environment-specific issues
- **Infrastructure Audits** - Compare public and private endpoints to confirm which RPC surfaces are intentionally exposed
- **Runtime Feature Gating** - Adjust tooling behavior dynamically based on the actual namespaces available on a node

## Best Practices

- Call at startup to determine which features are available on a node
- Module availability varies by node client and provider configuration
- Use to gate feature access in applications before attempting unsupported calls
- This is a non-standard method; some endpoints may not expose it

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_modules",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Object, required`): Object whose keys are enabled namespaces and whose values are version strings

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "eth": "1.0",
    "net": "1.0",
    "web3": "1.0",
    "rpc": "1.0",
    "debug": "1.0",
    "trace": "1.0",
    "txpool": "1.0"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_modules",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const modules = await provider.send('rpc_modules', []);
console.log('Namespaces:', Object.keys(modules));

if (modules.debug) {
  console.log('Debug RPC is enabled');
}
```

```python
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'rpc_modules',
        'params': [],
        'id': 1,
    },
)

modules = response.json()['result']
print('Namespaces:', sorted(modules.keys()))
print('Has trace:', 'trace' in modules)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "sort"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var modules map[string]string
    err = client.CallContext(context.Background(), &modules, "rpc_modules")
    if err != nil {
        log.Fatal(err)
    }

    names := make([]string, 0, len(modules))
    for name := range modules {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Printf("Namespaces: %v\n", names)
}
```

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Inspect the client software version string
- [`debug_traceTransaction`](https://www.dwellir.com/docs/berachain/debug_traceTransaction) - Debug namespace example
- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Trace namespace example

---

## trace_block - Berachain RPC Method

# trace_block - Berachain RPC Method

Returns traces for all transactions in a block on Berachain.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace all transactions in a block by block number** - Get the full trace of every transaction in a block on Berachain
- **Block-level execution analysis** - Inspect all internal calls, transfers, and contract interactions within a block for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **MEV research** - Analyze transaction ordering, sandwich patterns, and arbitrage across a full block
- **Historical block replay** - Replay and trace blocks at any point in Berachain chain history

## Best Practices

- Similar to trace\_replayBlockTransactions but without explicit replay configuration
- Use block hash variant (`debug_traceBlockByHash`) for reorg-safe queries
- Traces from dense blocks can be very large; process results in batches
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex or tag (latest, earliest, pending)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_block",
  "params": ["latest"],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_block",
    "params": ["latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_block', ['latest']);
console.log('Traces in block:', traces.length);
for (const trace of traces.slice(0, 5)) {
  console.log(`  ${trace.action.from} -> ${trace.action.to} (${trace.type})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_block', ['latest'])
for trace in traces['result'][:5]:
    action = trace['action']
    print(f'{action["from"]} -> {action.get("to", "CREATE")} ({trace["type"]})')
```

## Related Methods

- [`trace_filter`](https://www.dwellir.com/docs/berachain/trace_filter) - Filter traces by address or block range
- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Trace a specific transaction
- [`trace_get`](https://www.dwellir.com/docs/berachain/trace_get) - Get a specific trace by index

---

## trace_call - Berachain RPC Method

# trace_call - Berachain RPC Method

Traces a call without creating a transaction on Berachain, returning the trace output.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace simulated execution of multiple calls** - Preview internal calls before committing a transaction on Berachain
- **Pre-execution analysis** - Test contract interactions without spending gas for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Batch trace simulations** - Execute several calls sequentially where each can depend on prior state changes
- **Contract interaction analysis** - Understand how multiple contracts interact through a simulated execution chain

## Best Practices

- Similar to debug\_traceCall but supports multiple calls in one request
- Each call has its own trace configuration for fine-grained control
- Use for batch simulation analysis of dependent call sequences
- Requires archive node access for historical block tracing

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (from, to, gas, value, data)
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations
- `blockNumber` (`QUANTITY|TAG, optional`): Block number or tag (default: latest)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_call",
  "params": [
    {
      "to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8",
      "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"
    },
    ["trace"],
    "latest"
  ],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_call",
    "params": [
      {"to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8", "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"},
      ["trace"],
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const result = await provider.send('trace_call', [
  {
    to: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
    data: '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'
  },
  ['trace'],
  'latest'
]);
console.log('Trace output:', result.trace);
console.log('VM trace:', result.vmTrace);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

result = w3.provider.make_request('trace_call', [
    {
        'to': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8',
        'data': '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'
    },
    ['trace'],
    'latest'
])
print(f'Trace: {result["result"]["trace"]}')
```

## Related Methods

- [`trace_filter`](https://www.dwellir.com/docs/berachain/trace_filter) - Filter traces by criteria
- [`eth_call`](https://www.dwellir.com/docs/berachain/eth_call) - Execute call without trace
- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Trace a specific transaction

---

## trace_callMany - Berachain RPC Method

# trace_callMany - Berachain RPC Method

Traces multiple calls in sequence on Berachain, where each call can depend on the state changes of the previous one.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Trace multiple calls across different blocks** - Execute calls at different historical block heights for cross-block state analysis on Berachain
- **Historical state comparison** - Compare how the same call would execute at different points in Berachain chain history
- **Multi-step simulation** - Simulate a sequence of dependent calls where each step builds on the previous one for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Batch operation tracing** - Preview multiple contract interactions in a single RPC request

## Best Practices

- Each call specifies its own block number for cross-block analysis
- Useful for comparing state across time without multiple requests
- More efficient than separate trace\_call requests for multi-step workflows
- Requires archive node access for historical block state

## Request Parameters

- `calls` (`Array, required`): Array of [callObject, traceTypes] pairs
- `blockNumber` (`QUANTITY|TAG, optional`): Block number or tag (default: latest)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_callMany",
  "params": [
    [
      [{"to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8", "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"}, ["trace"]],
      [{"to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8", "data": "0x18160ddd"}, ["trace"]]
    ],
    "latest"
  ],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_callMany",
    "params": [
      [
        [{"to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8", "data": "0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8"}, ["trace"]],
        [{"to": "0x7507c1dc16935B82698e4C63f2746A2fCf994dF8", "data": "0x18160ddd"}, ["trace"]]
      ],
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const results = await provider.send('trace_callMany', [
  [
    [{ to: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', data: '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8' }, ['trace']],
    [{ to: '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', data: '0x18160ddd' }, ['trace']]
  ],
  'latest'
]);
console.log('Call results:', results.length);
for (const result of results) {
  console.log('  Output:', result.output);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

results = w3.provider.make_request('trace_callMany', [
    [
        [{'to': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', 'data': '0x70a082310000000000000000000000007507c1dc16935B82698e4C63f2746A2fCf994dF8'}, ['trace']],
        [{'to': '0x7507c1dc16935B82698e4C63f2746A2fCf994dF8', 'data': '0x18160ddd'}, ['trace']]
    ],
    'latest'
])
for i, result in enumerate(results['result']):
    print(f'Call {i}: output={result.get("output", "N/A")}')
```

## Related Methods

- [`trace_call`](https://www.dwellir.com/docs/berachain/trace_call) - Trace a single call
- [`eth_call`](https://www.dwellir.com/docs/berachain/eth_call) - Execute call without trace

---

## trace_filter - Berachain RPC Method

# trace_filter - Berachain RPC Method

Returns traces matching a filter on Berachain.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Search for traces matching address criteria** - Find all internal transactions involving a specific address on Berachain
- **Find all interactions with a contract** - Track every call, delegate call, and create operation targeting a contract for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Audit contract usage patterns** - Analyze how contracts are used over time across block ranges
- **Forensic analysis** - Investigate transaction patterns and address-level activity across the chain

## Best Practices

- Very resource-intensive on large block ranges; narrow the range as much as possible
- Use specific fromAddress or toAddress filters to reduce response size
- Limit block range to small chunks and paginate results
- Many provider-managed endpoints disable this method due to computational cost

## Request Parameters

- `filterObject` (`Object, required`): Filter criteria (see below)
- `fromBlock` (`QUANTITY|TAG, required`): Start block (hex or tag)
- `toBlock` (`QUANTITY|TAG, required`): End block (hex or tag)
- `fromAddress` (`Array<DATA>, required`): Filter by sender addresses
- `toAddress` (`Array<DATA>, required`): Filter by receiver addresses
- `after` (`QUANTITY, required`): Offset for pagination
- `count` (`QUANTITY, required`): Max results to return

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_filter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "toAddress": ["0x7507c1dc16935B82698e4C63f2746A2fCf994dF8"],
    "count": 10
  }],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_filter",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "toAddress": ["0x7507c1dc16935B82698e4C63f2746A2fCf994dF8"],
      "count": 10
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_filter', [{
  fromBlock: 'latest',
  toBlock: 'latest',
  toAddress: ['0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'],
  count: 10
}]);
console.log('Matching traces:', traces.length);
for (const trace of traces) {
  console.log(`  Block ${trace.blockNumber}: ${trace.action.from} -> ${trace.action.to}`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_filter', [{
    'fromBlock': 'latest',
    'toBlock': 'latest',
    'toAddress': ['0x7507c1dc16935B82698e4C63f2746A2fCf994dF8'],
    'count': 10
}])
for trace in traces['result']:
    action = trace['action']
    print(f'Block {trace["blockNumber"]}: {action["from"]} -> {action.get("to", "CREATE")}')
```

## Related Methods

- [`trace_block`](https://www.dwellir.com/docs/berachain/trace_block) - Get all traces in a block
- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Get traces for a specific transaction
- [`eth_getLogs`](https://www.dwellir.com/docs/berachain/eth_getLogs) - Filter event logs

---

## trace_get - Berachain RPC Method

# trace_get - Berachain RPC Method

Returns a trace at a specific position within a transaction on Berachain.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Get a specific trace by transaction hash and trace index** - Retrieve individual trace entries from a transaction on Berachain
- **Pinpoint specific internal calls** - Isolate a particular sub-call at a known position in the call tree for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Targeted debugging** - Investigate a specific call depth without retrieving the full transaction trace
- **Combine with trace\_transaction** - Discover trace indices from the full trace, then fetch details individually

## Best Practices

- Requires knowing the trace address and index in advance
- Combine with trace\_transaction to discover trace indices first
- Supports fetching multiple traces by passing an array of indices
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `indices` (`Array<QUANTITY>, required`): Trace index positions (e.g., ["0x0"] for the first trace)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_get",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", ["0x0"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_get",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", ["0x0"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const trace = await provider.send('trace_get', [
  '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671',
  ['0x0']
]);
console.log('Trace type:', trace.type);
console.log('From:', trace.action.from);
console.log('To:', trace.action.to);
console.log('Value:', trace.action.value);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('trace_get', [
    '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671',
    ['0x0']
])
result = trace['result']
print(f'Type: {result["type"]}')
print(f'From: {result["action"]["from"]}')
print(f'To: {result["action"]["to"]}')
```

## Related Methods

- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Get all traces for a transaction
- [`trace_block`](https://www.dwellir.com/docs/berachain/trace_block) - Get all traces in a block

---

## trace_replayBlockTransactions - Berachain RPC Method

# trace_replayBlockTransactions - Berachain RPC Method

Replays all transactions in a block on Berachain and returns the requested traces.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Replay all transactions in a block** - Get vmTrace, stateDiff, and trace for every transaction in a block on Berachain
- **Comprehensive block-level execution analysis** - Audit exactly how each transaction in a block modified state for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Audit entire block execution** - Verify that all transactions in a block executed as expected
- **Historical block replay** - Re-execute blocks at any point in Berachain chain history

## Best Practices

- Very resource-intensive; each transaction is fully traced with all requested types
- Limit to small blocks or use specific tracer types to reduce response size
- Request only the trace types you actually need to minimize overhead
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex or tag
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_replayBlockTransactions",
  "params": ["latest", ["trace"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_replayBlockTransactions",
    "params": ["latest", ["trace"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const results = await provider.send('trace_replayBlockTransactions', [
  'latest',
  ['trace']
]);
console.log('Transactions replayed:', results.length);
for (const result of results.slice(0, 3)) {
  console.log(`  Tx ${result.transactionHash}: ${result.trace.length} traces`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

results = w3.provider.make_request('trace_replayBlockTransactions', [
    'latest',
    ['trace']
])
for tx in results['result'][:3]:
    print(f'Tx {tx["transactionHash"]}: {len(tx["trace"])} traces')
```

## Related Methods

- [`trace_replayTransaction`](https://www.dwellir.com/docs/berachain/trace_replayTransaction) - Replay a single transaction
- [`trace_block`](https://www.dwellir.com/docs/berachain/trace_block) - Get traces without replay
- [`trace_filter`](https://www.dwellir.com/docs/berachain/trace_filter) - Filter traces by criteria

---

## trace_replayTransaction - Berachain RPC Method

# trace_replayTransaction - Berachain RPC Method

Replays a transaction on Berachain and returns the requested traces.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Replay and trace a transaction execution** - Get vmTrace, stateDiff, and trace in a single call for comprehensive analysis on Berachain
- **State diff extraction** - See exact account balance, nonce, code, and storage changes caused by a transaction for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **VM trace debugging** - Get opcode-level execution details alongside the structured call trace
- **Comprehensive transaction analysis** - Combine all three trace types in one request for complete execution visibility

## Best Practices

- Returns more detailed trace data than debug\_traceTransaction
- Combine vmTrace with trace array for a full picture of opcode and call-level execution
- Request all three trace types (trace, vmTrace, stateDiff) for maximum detail
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `traceTypes` (`Array, required`): Trace types: ["trace"], ["vmTrace"], ["stateDiff"], or combinations

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_replayTransaction",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", ["trace"]],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_replayTransaction",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671", ["trace"]],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const result = await provider.send('trace_replayTransaction', [
  '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671',
  ['trace']
]);
console.log('Trace:', result.trace.length, 'entries');
console.log('State diff:', result.stateDiff ? 'present' : 'not requested');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

result = w3.provider.make_request('trace_replayTransaction', [
    '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671',
    ['trace']
])
trace = result['result']
print(f'Trace entries: {len(trace["trace"])}')
```

## Related Methods

- [`trace_replayBlockTransactions`](https://www.dwellir.com/docs/berachain/trace_replayBlockTransactions) - Replay all transactions in a block
- [`trace_transaction`](https://www.dwellir.com/docs/berachain/trace_transaction) - Get traces without replay
- [`trace_block`](https://www.dwellir.com/docs/berachain/trace_block) - Get all traces in a block

---

## trace_transaction - Berachain RPC Method

# trace_transaction - Berachain RPC Method

Returns all traces for a specific transaction on Berachain.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Get parity-style transaction trace** - Retrieve the full trace of all internal calls, state changes, and value transfers for liquidity-aligned DeFi (Infrared, Kodiak), yield farming, and validator-integrated liquidity incentives
- **Analyze internal calls and state changes** - See every sub-call, delegate call, and contract creation triggered by a transaction on Berachain
- **Audit transaction execution paths** - Follow the exact flow of execution through contracts to verify correctness
- **Track value flows** - Trace how funds move through multiple contracts in a single transaction

## Best Practices

- Parity-style traces are more detailed than the debug namespace equivalent
- Use trace\_replayTransaction for combined trace, vmTrace, and stateDiff output
- Results include both stateDiff and vmTrace sections for comprehensive analysis
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "trace_transaction",
  "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "trace_transaction",
    "params": ["0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const traces = await provider.send('trace_transaction', [
  '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671'
]);
console.log('Traces:', traces.length);
for (const trace of traces) {
  console.log(`  ${trace.action.from} -> ${trace.action.to} (${trace.type})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('trace_transaction', [
    '0xc5c421e9e1f89c46d6f23e4057f24ed39fe8c8d75f0dc27159b46f8b6ece3671'
])
for trace in traces['result']:
    action = trace['action']
    print(f'{action["from"]} -> {action.get("to", "CREATE")} ({trace["type"]})')
```

## Related Methods

- [`trace_get`](https://www.dwellir.com/docs/berachain/trace_get) - Get a specific trace by index
- [`trace_block`](https://www.dwellir.com/docs/berachain/trace_block) - Get all traces in a block
- [`trace_filter`](https://www.dwellir.com/docs/berachain/trace_filter) - Filter traces by criteria

---

## txpool_content - Berachain RPC Method

# txpool_content - Berachain RPC Method

Returns the full pending and queued transaction pool for the connected Berachain endpoint. Transactions are grouped first by sender address and then by nonce.

> **Non-standard method.** `txpool_content` is a Geth-style mempool inspection method. It is not part of the core Ethereum Execution API method set, and many shared RPC endpoints disable it because of response size and sensitivity concerns.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`txpool_content` is useful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Replacement Transaction Debugging** - Inspect multiple transactions with the same sender and nonce
- **Mempool Analytics** - Analyze which accounts dominate pending flow and how backlogs are distributed
- **Relayer Operations** - Verify whether submitted transactions are still pending, queued, or replaced
- **Fee Strategy Tuning** - Inspect real mempool fee levels and transaction types across the pending pool

## Best Practices

- Response can be very large on congested networks; be prepared to handle large payloads
- Use txpool\_status for summary statistics instead when you do not need full content
- Filter results client-side for specific addresses of interest to reduce noise
- This is a non-standard method; many shared endpoints disable this for performance reasons

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "txpool_content",
  "params": [],
  "id": 1
}
```

## Response Fields

- `pending` (`Object, required`): Address-indexed map of processable transactions grouped by nonce
- `queued` (`Object, required`): Address-indexed map of non-processable transactions grouped by nonce

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "pending": {
      "0x000000cd5e2aa28b0fbb66219756f36e318a4ed7": {
        "94": {
          "from": "0x000000cd5e2aa28b0fbb66219756f36e318a4ed7",
          "to": "0xe88b4ac89a986048e48e48ff019eee4281a9791f",
          "nonce": "0x5e",
          "gas": "0x7a120",
          "gasPrice": "0x5d21dba00",
          "maxFeePerGas": "0x5d21dba00",
          "maxPriorityFeePerGas": "0x5d21dba00",
          "hash": "0xeb0eb7b61fd61739bfd87667fd787eca81d3af18bb67eaced20a7e1c0f798532",
          "value": "0x0",
          "type": "0x2"
        }
      }
    },
    "queued": {}
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "txpool_content",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const txpool = await provider.send('txpool_content', []);
const pendingAccounts = Object.keys(txpool.pending);
const queuedAccounts = Object.keys(txpool.queued);

console.log('Pending accounts:', pendingAccounts.length);
console.log('Queued accounts:', queuedAccounts.length);

if (pendingAccounts.length > 0) {
  const firstAccount = pendingAccounts[0];
  const firstNonce = Object.keys(txpool.pending[firstAccount])[0];
  console.log('Sample pending tx:', txpool.pending[firstAccount][firstNonce]);
}
```

```python
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'txpool_content',
        'params': [],
        'id': 1,
    },
)

txpool = response.json()['result']
pending_accounts = list(txpool['pending'].keys())
queued_accounts = list(txpool['queued'].keys())

print('Pending accounts:', len(pending_accounts))
print('Queued accounts:', len(queued_accounts))
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var txpool map[string]map[string]map[string]map[string]any
    err = client.CallContext(context.Background(), &txpool, "txpool_content")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending accounts: %d\n", len(txpool["pending"]))
    fmt.Printf("Queued accounts: %d\n", len(txpool["queued"]))
}
```

## Related Methods

- [`txpool_status`](https://www.dwellir.com/docs/berachain/txpool_status) - Retrieve only pending and queued counters
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/berachain/eth_getTransactionByHash) - Inspect a single transaction after you identify it in the mempool
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/berachain/eth_sendRawTransaction) - Broadcast signed transactions to the network

---

## txpool_status - Berachain RPC Method

# txpool_status - Berachain RPC Method

Returns transaction pool counters for the connected Berachain endpoint. The result separates transactions that are immediately processable (`pending`) from those waiting on an earlier nonce or other prerequisite (`queued`).

> **Non-standard method.** `txpool_status` is a Geth-style mempool inspection method. It is not part of the core Ethereum Execution API method set, and many shared RPC endpoints disable it.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`txpool_status` is valuable for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Mempool Monitoring** - Watch pending versus queued pressure on a node
- **Congestion Signals** - Detect bursts of transaction backlog before they show up in block-level metrics
- **Node Health Checks** - Confirm a node is accepting and classifying new transactions as expected
- **Operational Dashboards** - Surface lightweight txpool counters without pulling full transaction content

## Best Practices

- Returns pending and queued transaction counts; high pending counts suggest network congestion
- Combine with eth\_gasPrice for informed transaction submission timing decisions
- This is a non-standard method; many shared endpoints disable txpool access
- Use as a lightweight alternative to txpool\_content when you only need aggregate counts

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "txpool_status",
  "params": [],
  "id": 1
}
```

## Response Fields

- `pending` (`QUANTITY, required`): Number of processable transactions currently in the pool
- `queued` (`QUANTITY, required`): Number of transactions waiting on an earlier prerequisite such as nonce order

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "pending": "0xbaf3",
    "queued": "0x1b6"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "txpool_status",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const txpool = await provider.send('txpool_status', []);

console.log('Pending:', parseInt(txpool.pending, 16));
console.log('Queued:', parseInt(txpool.queued, 16));
```

```python
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'txpool_status',
        'params': [],
        'id': 1,
    },
)

txpool = response.json()['result']
print('Pending:', int(txpool['pending'], 16))
print('Queued:', int(txpool['queued'], 16))
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var status map[string]string
    err = client.CallContext(context.Background(), &status, "txpool_status")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %s\n", status["pending"])
    fmt.Printf("Queued: %s\n", status["queued"])
}
```

## Related Methods

- [`txpool_content`](https://www.dwellir.com/docs/berachain/txpool_content) - Inspect the full pending and queued transaction maps
- [`eth_blockNumber`](https://www.dwellir.com/docs/berachain/eth_blockNumber) - Track block production alongside mempool pressure
- [`eth_gasPrice`](https://www.dwellir.com/docs/berachain/eth_gasPrice) - Compare congestion signals with fee estimates

---

## web3_clientVersion - Berachain RPC Method

Returns the current client software version string for your Berachain node, including the client name, version number, OS, and runtime.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

## When to Use This Method

`web3_clientVersion` is valuable for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Berachain client:', clientVersion);

// Using fetch
const response = await fetch('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Berachain client: {client_version}')

# web3_clientVersion - Berachain RPC Method
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Berachain client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/berachain/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/berachain/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/berachain/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/berachain/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Berachain RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Berachain.

> **Why Berachain?** Build on the Proof-of-Liquidity L1 with $3.2B+ TVL and innovative three-token economics with Proof-of-Liquidity consensus, three-token model (BERA/BGT/HONEY), $142M funding, and unified validator-DeFi incentive alignment.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for DeFi protocol developers, liquidity providers, and teams building yield-optimized applications:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Berachain RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-berachain-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/berachain/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/berachain/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/berachain/web3_clientVersion) - Get node client version

---

## Bifrost Liquid Staking Parachain RPC Guide

## Why Build on Bifrost?

Bifrost is Polkadot's liquid staking hub, issuing omni-chain vTokens that unlock staking liquidity while keeping rewards flowing. Builders target Bifrost because it delivers:

### **Omni-Chain Liquidity Routing**

- Staking Liquidity Protocol (SLP) mints yield-bearing vTokens (vDOT, vKSM, vETH, vFIL) that stay composable across parachains and EVM networks.
- Slot Auction Liquidity Protocol (SALP) releases DOT/KSM crowdloan positions into tradable derivatives, letting users support auctions without sacrificing liquidity.
- Liquid staking primitives integrate with major DeFi venues (Pendle, Loop Stake, Talisman, etc.) to optimize leverage and hedging strategies.

### **Protocol-Neutral Architecture**

- Dedicated pallets for cross-chain execution (ISMP, CrossInOut, Hyperbridge) make it easy to bridge liquidity between Polkadot, Kusama, and external EVMs.
- Governance and fee-share pallets align collators, liquidity providers, and derivative users through on-chain revenue distribution.
- Runtime upgrades (v0.21.1 / specVersion 21001) focus on async backing, Hyperbridge routes, and tokenomics 2.0 for sustained reward flows.

### **Battle-Tested Operations**

- Active collator set with \~6 s block time keeps derivatives in sync with relay-chain finality.
- Proven SALP campaigns unlock DOT crowdloans while maintaining reward accrual.
- Continuous grant support and ecosystem partnerships accelerate tooling and collateral adoption.

## Quick Start with Bifrost

Connect to mainnet or the Kusama canary network in seconds using Dwellir-managed infrastructure.

The Kusama deployment (Para ID 2001) runs identical pallets ahead of Polkadot releases. Use it for SALP dry-runs, Hyperbridge testing, and upgrade rehearsals before promoting to mainnet Para ID 2030.

### Installation & Setup

cURL
JavaScript (polkadot.js)
Rust (subxt)
Python (py-substrate-interface)

```bash
curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "chain_getHeader",
    "params": []
  }'
```

**Expected output (captured 2025-10-03):**

```json
{
  "jsonrpc": "2.0",
  "result": {
    "number": "0x9055f2",
    "hash": "0xd4b3a0e3b88b7c25215926bbf7c733f5c31e69c0c8c4b4a86d6ec3f3f086e95d",
    "parentHash": "0x65b246e9b69a61f842104a15307ae906013c56a8bb6c942555b1a71424b4bab3",
    "stateRoot": "0x220ea5bd35df99176268d8cbcee287a8a81a2e8addfa764661f71af591db0329",
    "extrinsicsRoot": "0x388e5e2bb951b1dfac3f6a3f0c1d5de1ace6e413008efe48f1a54228dcd049db"
  },
  "id": 1
}
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function main() {
  const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
  const api = await ApiPromise.create({ provider });

  const [chain, nodeName, nodeVersion, runtime] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.state.getRuntimeVersion()
  ]);

  console.log(`Connected to ${chain.toString()} via ${nodeName.toString()} ${nodeVersion.toString()}`);
  console.log(`Runtime specVersion=${runtime.specVersion.toNumber()} transactionVersion=${runtime.transactionVersion.toNumber()}`);

  const header = await api.rpc.chain.getHeader();
  console.log(`Latest head #${header.number.toString()} (${header.hash.toHex()})`);

  await api.disconnect();
}

main().catch(console.error);
```

```rust
use subxt::{config::substrate::SubstrateConfig, OnlineClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<SubstrateConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let latest = api.rpc().header(None).await?.expect("header");
    println!("Finalized block #{}", latest.number);
    println!("State root {}", latest.state_root);

    Ok(())
}
```

```python
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(
    url="wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY",
    type_registry_preset="substrate-node-template"
)

latest_hash = substrate.get_chain_head()
print("Latest head:", latest_hash)

runtime = substrate.get_runtime_version()
print("specVersion:", runtime['specVersion'], "transactionVersion:", runtime['transactionVersion'])

account_info = substrate.query(
    module='System',
    storage_function='Account',
    params=['15mYsj6DpBno58jRoV5HCTiVPFBuWhDLdsWtq3LxwZrfaTEZ']
)
print("Collator free balance:", account_info.value['data']['free'])
```

## API Reference

Bifrost exposes the standard Substrate namespaces for node telemetry, finality, storage, extrinsic submission, and runtime metadata, alongside specialized pallets such as `Hyperbridge`, `FlexibleFee`, `Farming`, and `Ismp`.

## Network Information

| Parameter                | Bifrost (Polkadot Mainnet)                                           | Bifrost (Kusama Canary)                                              |
| ------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
| **Relay Chain**          | Polkadot                                                             | Kusama                                                               |
| **Parachain ID**         | 2030                                                                 | 2001                                                                 |
| **Genesis Hash**         | `0x262e1b2ad728475fd6fe88e62d34c200abe6fd693931ddad144059b1eb884e5b` | `0x9f28c6a68e0fc9646eff64935684f6eeeece527e37bbe1f213d22caa1d9d6bed` |
| **Runtime (2025-10-03)** | specVersion `21001`, transactionVersion `1`                          | specVersion `19000`, transactionVersion `1`                          |
| **Unit Symbol**          | BNC                                                                  | BNC                                                                  |
| **Decimals**             | 12                                                                   | 12                                                                   |
| **SS58 Prefix**          | 6                                                                    | 6                                                                    |
| **Explorer**             | bifrost.subscan.io                                                   | bifrost-kusama.subscan.io                                            |
| **Average Block Time**   | \~6 seconds                                                          | \~6 seconds                                                          |

### Additional Details

| Parameter         | Value                                               | Details                                        |
| ----------------- | --------------------------------------------------- | ---------------------------------------------- |
| Collator Rotation | >16 active authors                                  | RefTime utilization stays under 0.5% per block |
| Runtime Pallets   | `Hyperbridge`, `FlexibleFee`, `Farming`, and `Ismp` | Cross-ecosystem liquidity orchestration        |

## Common Integration Patterns

### Real-Time Block Streaming

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New Bifrost block #${header.number.toString()} (${header.hash.toHex()})`);
});
```

### Query Staking Liquidity Positions

```bash
# Bifrost Liquid Staking Parachain RPC Guide
curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "id":42,
    "method":"state_getStorage",
    "params":[
      "0x1bd4a27d04bdb9c3f135c3b09677626d26c37dd72647d0f2788a3341e9ac1371"
    ]
  }'
```

### Estimate Fees for SALP Transfers

```typescript
const api = await ApiPromise.create({ provider: new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY') });

const tx = api.tx.crossInOut.swapExactAssetsForAssets(
  /* asset_in */ { Xcm: { V3: { parents: 1, interior: 'Here' } } },
  /* asset_out */ { Token: 'BNC' },
  /* amount */ 1_000_000_000_000n,
  /* min_receive */ 990_000_000_000n
);

const info = await api.rpc.payment.queryInfo(tx.toHex());
console.log(`Partial fee: ${info.partialFee.toHuman()}`);
console.log(`Weight: ${info.weight.refTime.toString()} refTime`);
```

## Performance Best Practices

- Prefer WebSocket endpoints for subscriptions and long-lived queries; use HTTPS for bursty read-only workloads.
- Cache runtime metadata and type registries keyed by `specVersion` (Polkadot `21001`, Kusama `19000` as of 2025-10-03) to avoid repeated handshakes.
- Use `state_getKeysPaged` with sized pages (<1024 keys) when scanning liquidity pools or reward ledgers.
- Pin to finalized heads for settlement-critical reads; leverage `chain_getFinalizedHead` before fetching block data.
- Implement jittered reconnect backoff (250 ms → 8 s) to respect Dwellir rate limiting during failover events.

## Troubleshooting

| Symptom                                                       | Likely Cause                                                       | Resolution                                                                                     |
| ------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| `1010: Invalid Transaction` when submitting vToken extrinsics | Runtime upgrade bumped `specVersion`/`transactionVersion`          | Refresh metadata, rebuild the payload, and resubmit with the updated version info              |
| `Invalid SS58 address` errors                                 | Using Polkadot prefix (`0`) for Bifrost accounts                   | Re-derive addresses with SS58 prefix `6` before signing or decoding                            |
| `TypeError: Cannot decode storage`                            | Missing custom pallets (Hyperbridge, FlexibleFee) in type registry | Extend your type bundle with the latest Bifrost runtime metadata                               |
| Persistent WebSocket disconnects                              | Idle connection without keep-alives                                | Send periodic heartbeats (e.g., `rpc_methods`) or enable `WsProvider` autoConnect              |
| `Authoring not allowed for account` on collator nodes         | Rotate keys not submitted after upgrade                            | Call `author_rotateKeys` and `parachainStaking.setKeys` on the canary network, then on mainnet |

## Smoke Tests

Run these checks before pushing to production:

1. **Node health**
   ```bash
   curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
     -H "Content-Type: application/json" \
     -d '{"jsonrpc":"2.0","id":1,"method":"system_health","params":[]}'
   ```
   Verify `isSyncing` is `false` and peer count stays above 8.

2. **Latest header increments**
   ```bash
   curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
     -H "Content-Type: application/json" \
     -d '{"jsonrpc":"2.0","id":2,"method":"chain_getHeader","params":[]}'
   ```
   Confirm block numbers advance \~every 6 seconds (e.g., #9,053,714 at 2025-10-03 07:42:18 UTC).

3. **Account snapshot**
   ```bash
   curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
     -H "Content-Type: application/json" \
     -d '{"jsonrpc":"2.0","id":3,"method":"state_getStorage","params":["0x26aa394eea5630e07c48ae0c9558cef7f0d3720dbb6f7b3bb82b41c16dbf8d0887f21b38c918a1962fa4273d0f3c2c23244cc25f029aba80a72f1ac277957bc4"]}'
   ```
   Decode the SCALE payload to check BNC balances (collator `15mYsj6…TEZ` shows active staking reserves).

## Migration Guide (Polkadot/Westend → Bifrost)

- **Endpoints:** Swap legacy Polkadot URLs for `https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY`. Use the Kusama canary endpoint for staging.
- **Address encoding:** Update wallets and services to SS58 prefix `6` for Bifrost accounts.
- **Metadata:** Refresh runtime metadata whenever `specVersion` changes (currently `21001` on Polkadot, `19000` on Kusama). Update custom pallets (Hyperbridge, FlexibleFee, SALP) in your type registry.
- **Fee estimation:** Run `payment_queryInfo` for vToken transfers; Bifrost’s flexible-fee pallet introduces dynamic surcharge multipliers.
- **Pallet coverage:** Check `rpc_methods` after upgrades to discover new `bifrost_*` runtime APIs (e.g., omnipool analytics, Hyperbridge routing).

## Resources & Tools

- Bifrost network explorer: [bifrost.subscan.io](https://bifrost.subscan.io)
- Bifrost GitHub releases and runtime notes: [github.com/bifrost-io/bifrost/releases](https://github.com/bifrost-io/bifrost/releases)
- Dwellir Dashboard: [dashboard.dwellir.com/register](https://dashboard.dwellir.com/register)
- Community updates and integration stories: [parachains.info/details/bifrost\_finance\_polkadot](https://parachains.info/details/bifrost_finance_polkadot)

Plan your integration, mint liquidity-backed vTokens, and rely on Dwellir’s globally distributed RPC edge to keep your Bifrost workloads online.

---

## author_pendingExtrinsics - Bifrost RPC Method

Returns all pending extrinsics currently in the transaction pool on Bifrost. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`author_pendingExtrinsics` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Bifrost
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Bifrost transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Bifrost RPC Method

Generate a new set of session keys on Bifrost. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`author_rotateKeys` is critical for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Validator Setup** - Generate initial session keys when setting up a new validator on omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Bifrost RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Bifrost:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bifrost/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Bifrost RPC Method

Submits a signed extrinsic to Bifrost and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Bifrost
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Bifrost RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bifrost/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Bifrost RPC Method

Submits a fully signed extrinsic to Bifrost for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`author_submitExtrinsic` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Token Transfers** -- Send native tokens or assets between accounts on Bifrost
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Bifrost RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bifrost/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Estimate fees before submitting
- `system_accountNextIndex` -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Bifrost RPC Method

# beefy_getFinalizedHead - Bifrost RPC Method

Returns the block hash of the latest BEEFY-finalized block on Bifrost. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`beefy_getFinalizedHead` is important for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Cross-Chain Bridges** - Verify finality proofs efficiently for bridge operations on omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Light Clients** - Verify finality without downloading full GRANDPA justifications
- **Trustless Bridges** - Generate compact finality proofs that can be verified on external chains
- **Bridge Monitoring** - Track BEEFY finality progress relative to GRANDPA finality

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest BEEFY-finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response (BEEFY Not Enabled)

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "BEEFY is not enabled on this chain"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  // Get BEEFY finalized head
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  console.log('BEEFY finalized:', beefyHead.toHex());

  // Compare with GRANDPA finalized
  const grandpaHead = await api.rpc.chain.getFinalizedHead();
  console.log('GRANDPA finalized:', grandpaHead.toHex());

  // Get block numbers for comparison
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const grandpaBlock = await api.rpc.chain.getBlock(grandpaHead);

  const beefyNum = beefyBlock.block.header.number.toNumber();
  const grandpaNum = grandpaBlock.block.header.number.toNumber();
  console.log(`BEEFY lag behind GRANDPA: ${grandpaNum - beefyNum} blocks`);
} catch (error) {
  console.error('BEEFY may not be enabled:', error.message);
}

await api.disconnect();
```

```python
import requests

def get_beefy_finalized_head():
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'beefy_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"BEEFY error: {result['error']['message']}")

    return result['result']

def get_grandpa_finalized_head():
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 2
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

try:
    beefy_hash = get_beefy_finalized_head()
    grandpa_hash = get_grandpa_finalized_head()
    print(f'BEEFY finalized: {beefy_hash}')
    print(f'GRANDPA finalized: {grandpa_hash}')
except Exception as e:
    print(f'Error: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Call beefy_getFinalizedHead via raw RPC
    let beefy_head: Value = api.rpc()
        .request("beefy_getFinalizedHead", subxt::rpc_params![])
        .await?;

    println!("BEEFY finalized: {}", beefy_head);

    // Compare with GRANDPA finalized
    let grandpa_head = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("GRANDPA finalized: {:?}", grandpa_head);

    Ok(())
}
```

## Common Use Cases

### 1. Bridge Finality Verification

Verify BEEFY finality before relaying messages on a cross-chain bridge:

```javascript
async function verifyBridgeFinality(api, targetBlockHash) {
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const beefyNumber = beefyBlock.block.header.number.toNumber();

  const targetBlock = await api.rpc.chain.getBlock(targetBlockHash);
  const targetNumber = targetBlock.block.header.number.toNumber();

  if (beefyNumber >= targetNumber) {
    console.log(`Block #${targetNumber} has BEEFY finality - safe to relay`);
    return true;
  } else {
    console.log(`Waiting: BEEFY at #${beefyNumber}, target at #${targetNumber}`);
    return false;
  }
}
```

### 2. BEEFY vs GRANDPA Finality Monitor

Track the gap between the two finality gadgets:

```javascript
async function monitorFinalityGadgets(api) {
  setInterval(async () => {
    try {
      const [beefyHead, grandpaHead] = await Promise.all([
        api.rpc.beefy.getFinalizedHead(),
        api.rpc.chain.getFinalizedHead()
      ]);

      const [beefyBlock, grandpaBlock] = await Promise.all([
        api.rpc.chain.getBlock(beefyHead),
        api.rpc.chain.getBlock(grandpaHead)
      ]);

      const beefyNum = beefyBlock.block.header.number.toNumber();
      const grandpaNum = grandpaBlock.block.header.number.toNumber();
      const lag = grandpaNum - beefyNum;

      console.log(`GRANDPA: #${grandpaNum} | BEEFY: #${beefyNum} | Lag: ${lag} blocks`);
    } catch (error) {
      console.error('Monitor error:', error.message);
    }
  }, 12000);
}
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/bifrost/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Bifrost RPC Method

Retrieves complete block information from Bifrost, including the block header, extrinsics, and justifications.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Bifrost RPC Method
curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/bifrost/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/bifrost/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Bifrost RPC Method

Returns the block hash for a given block number on Bifrost. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`chain_getBlockHash` is fundamental for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Bifrost
- **Block Navigation** - Navigate the blockchain history for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Bifrost
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Bifrost for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Bifrost state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Bifrost network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/bifrost/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Bifrost RPC Method

Returns the hash of the last finalized block on Bifrost. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`chain_getFinalizedHead` is critical for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Bifrost
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Bifrost RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Bifrost:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bifrost/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/bifrost/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHeader - Bifrost RPC Method

Returns the block header for a given hash on Bifrost. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`chain_getHeader` is ideal for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Bifrost
- **Chain Synchronization** - Track block production and monitor chain progress for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Bifrost:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Bifrost chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Bifrost block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bifrost/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_subscribeFinalizedHeads - Bifrost RPC Method

Subscribe to receive notifications when blocks are finalized on Bifrost. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Exchange Deposits** - Only credit funds after finalization for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Bifrost RPC Method
wscat -c wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Bifrost:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/bifrost/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Bifrost RPC Method

Subscribe to receive notifications when new block headers are produced on Bifrost. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`chain_subscribeNewHeads` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Block Monitoring** - Track new blocks in real time on Bifrost for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Bifrost RPC Method
wscat -c wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Bifrost as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Bifrost:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/bifrost/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/bifrost/chain_getBlock) - Get full block data with extrinsics
- `chain_unsubscribeNewHeads` - Unsubscribe from new heads

---

## grandpa_roundState - Bifrost RPC Method

Returns the state of the current GRANDPA finality round on Bifrost when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`grandpa_roundState` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Finality Monitoring** -- Track whether GRANDPA rounds are progressing normally or stalling on Bifrost, critical for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Consensus Health Checks** -- Detect finality delays by comparing prevote/precommit counts against the supermajority threshold weight
- **Validator Participation Analysis** -- Monitor which validators are actively voting and whether the authority set has sufficient online weight
- **Authority Set Tracking** -- Observe `setId` changes after validator set rotations to verify smooth authority transitions
- **Capability Detection** -- Confirm whether the shared endpoint exposes GRANDPA round internals before you build monitoring around them

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

- `setId` (`u64, required`): The current GRANDPA authority set ID; increments when the validator set changes
- `best` (`RoundState, required`): State of the best (most recent) active round
- `background` (`Vec<RoundState>, required`): Background rounds that are still being tracked (typically the previous round)
- `round` (`u64, required`): The round number
- `totalWeight` (`u64, required`): Total combined weight of all authorities in this set
- `thresholdWeight` (`u64, required`): Minimum weight required for a supermajority (2/3 + 1 of totalWeight)
- `prevotes` (`Votes, required`): Current prevote state for this round
- `precommits` (`Votes, required`): Current precommit state for this round
- `currentWeight` (`u64, required`): Total weight of votes received so far
- `missing` (`Vec<AuthorityId>, required`): List of authority public keys that have not yet voted

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "setId": 4821,
    "best": {
      "round": 19384,
      "totalWeight": 297,
      "thresholdWeight": 199,
      "prevotes": {
        "currentWeight": 297,
        "missing": []
      },
      "precommits": {
        "currentWeight": 264,
        "missing": [
          "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
          "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
        ]
      }
    },
    "background": []
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  const roundState = await api.rpc.grandpa.roundState();
  const best = roundState.best;

  console.log('Authority set ID:', roundState.setId.toString());
  console.log('Round:', best.round.toString());
  console.log('Total weight:', best.totalWeight.toString());
  console.log('Threshold weight:', best.thresholdWeight.toString());
  console.log('Prevote weight:', best.prevotes.currentWeight.toString());
  console.log('Precommit weight:', best.precommits.currentWeight.toString());
  console.log('Missing precommits:', best.precommits.missing.length);
} catch (error) {
  console.log('grandpa_roundState unsupported:', error.message);
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('grandpa_roundState unsupported:', payload.error.message);
} else {
  console.log('Set ID:', payload.result.setId);
  console.log('Best round:', payload.result.best.round);
  console.log('Prevote progress:', payload.result.best.prevotes.currentWeight, '/', payload.result.best.thresholdWeight);
  console.log('Precommit progress:', payload.result.best.precommits.currentWeight, '/', payload.result.best.thresholdWeight);
}
```

```python
import requests

def get_grandpa_round_state():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'grandpa_roundState',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

try:
    state = get_grandpa_round_state()
    best = state['best']

    print(f"Authority set ID: {state['setId']}")
    print(f"Round: {best['round']}")
    print(f"Prevote: {best['prevotes']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Precommit: {best['precommits']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Missing precommit voters: {len(best['precommits']['missing'])}")
except KeyError:
    print('grandpa_roundState unsupported on this endpoint')

# grandpa_roundState - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
response = substrate.rpc_request('grandpa_roundState', [])
if 'error' in response:
    print(f"grandpa_roundState unsupported: {response['error']['message']}")
else:
    print(f"Set ID: {response['result']['setId']}, Round: {response['result']['best']['round']}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
    set_id: u64,
    best: BestRound,
    background: Vec<BestRound>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct BestRound {
    round: u64,
    total_weight: u64,
    threshold_weight: u64,
    prevotes: Votes,
    precommits: Votes,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Votes {
    current_weight: u64,
    missing: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "grandpa_roundState",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    if body.get("error").is_some() {
        println!("grandpa_roundState unsupported: {}", body["error"]["message"]);
        return Ok(());
    }

    let state: RoundState = serde_json::from_value(body["result"].clone())?;

    println!("Set ID: {}", state.set_id);
    println!("Round: {}", state.best.round);
    println!("Prevote: {}/{}", state.best.prevotes.current_weight, state.best.threshold_weight);
    println!("Precommit: {}/{}", state.best.precommits.current_weight, state.best.threshold_weight);
    println!("Missing precommit voters: {}", state.best.precommits.missing.len());

    Ok(())
}
```

## Common Use Cases

### 1. Finality Health Monitoring

Periodically check whether GRANDPA rounds are progressing and alert on stalls:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorFinality(api, intervalMs = 10000) {
  let lastRound = 0;
  let lastSetId = 0;
  let stallCount = 0;

  setInterval(async () => {
    const state = await api.rpc.grandpa.roundState();
    const best = state.best;
    const round = best.round.toNumber();
    const setId = state.setId.toNumber();
    const prevoteProgress = best.prevotes.currentWeight.toNumber();
    const precommitProgress = best.precommits.currentWeight.toNumber();
    const threshold = best.thresholdWeight.toNumber();

    if (setId !== lastSetId) {
      console.log(`Authority set changed: ${lastSetId} -> ${setId}`);
      lastSetId = setId;
    }

    if (round === lastRound) {
      stallCount++;
      if (stallCount >= 3) {
        console.warn(`GRANDPA round ${round} stalled for ${stallCount} checks`);
        console.warn(`  Prevotes: ${prevoteProgress}/${threshold}`);
        console.warn(`  Precommits: ${precommitProgress}/${threshold}`);
        console.warn(`  Missing voters: ${best.precommits.missing.length}`);
      }
    } else {
      stallCount = 0;
      console.log(`Round ${round} | prevotes=${prevoteProgress}/${threshold} precommits=${precommitProgress}/${threshold}`);
    }

    lastRound = round;
  }, intervalMs);
}
```

### 2. Validator Participation Report

Generate a report of which validators are consistently missing votes:

```javascript
async function trackMissingVoters(api, samples = 20, delayMs = 6000) {
  const missingCounts = {};

  for (let i = 0; i < samples; i++) {
    const state = await api.rpc.grandpa.roundState();
    const missing = state.best.precommits.missing;

    missing.forEach((authority) => {
      const key = authority.toString();
      missingCounts[key] = (missingCounts[key] || 0) + 1;
    });

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  // Sort by most frequently missing
  const sorted = Object.entries(missingCounts)
    .sort(([, a], [, b]) => b - a);

  console.log('Validator participation report:');
  sorted.forEach(([authority, count]) => {
    const missRate = ((count / samples) * 100).toFixed(1);
    console.log(`  ${authority}: missed ${count}/${samples} (${missRate}%)`);
  });

  return sorted;
}
```

### 3. Supported-Fallback Check

If the endpoint does not expose GRANDPA round internals, fall back to finalized-head tracking:

```javascript
async function getFinalitySignal(api) {
  try {
    return { supported: true, roundState: await api.rpc.grandpa.roundState() };
  } catch (error) {
    return {
      supported: false,
      finalizedHead: (await api.rpc.chain.getFinalizedHead()).toHex(),
      message: error.message
    };
  }
}
```

### 3. Finality Lag Detection

Compare the finalized head with the best block to measure finality lag:

```javascript
async function getFinalityLag(api) {
  const [roundState, finalizedHash, bestHeader] = await Promise.all([
    api.rpc.grandpa.roundState(),
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader()
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);
  const bestNumber = bestHeader.number.toNumber();
  const finalizedNumber = finalizedHeader.number.toNumber();
  const lag = bestNumber - finalizedNumber;

  return {
    bestBlock: bestNumber,
    finalizedBlock: finalizedNumber,
    lagBlocks: lag,
    grandpaRound: roundState.best.round.toNumber(),
    setId: roundState.setId.toNumber(),
    prevoteReached: roundState.best.prevotes.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber(),
    precommitReached: roundState.best.precommits.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber()
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/bifrost/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/bifrost/system_health) -- Check overall node health including sync and peer status

---

## payment_queryFeeDetails - Bifrost RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Bifrost. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`payment_queryFeeDetails` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Bifrost
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Bifrost RPC Method

Estimates the fee for an encoded extrinsic on Bifrost. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`payment_queryInfo` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Bifrost
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Bifrost RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bifrost/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/bifrost/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bifrost/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Bifrost RPC Method

Returns a list of all RPC methods exposed by the Bifrost node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`rpc_methods` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Bifrost node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Bifrost endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Bifrost endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bifrost/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Bifrost RPC Method

Calls a runtime API function on Bifrost and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_call` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bifrost/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bifrost/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/bifrost/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) -- Get the node version for compatibility checking

---

## state_getKeys

# state_getKeys

## Description

Returns all storage keys with a given prefix. Use this to discover accounts, vToken positions, or other on-chain items before fetching values with `state_getStorage`.

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage key prefix
- `blockHash` (`string, optional`): Block hash for historical queries

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "<prefix>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`OBJECT, required`): Array of matching hex-encoded storage keys.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b6a7c9a35a95a160cb4cad330558dfa56d6f646c755b98a7afd0913860592153322b957be394a0619f20b32679ac1014",
    "0x26aa394eea5630e07c48ae0c9558cef7c8e63e63a001c280f6dc809625f6ce9c53c51e645dd0a2c3e0800a9c53aac6ffe81912f6526f364eb8e4a36c2e304931"
  ],
  "id": 1
}
```

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7",
    null
  ],
  "id": 1
}
```

## Code Examples

Python
JavaScript

```python
keys = substrate.rpc_request(
    method='state_getKeys',
    params=['0x26aa394eea5630e07c48ae0c9558cef7', None]
)["result"]
print(len(keys))
```

```javascript
const keyPrefix = api.registry.createType('StorageKey', 'System', 'Account').toHex();
const keys = await api.rpc.state.getKeys(keyPrefix, null);
console.log('Found', keys.length, 'System.Account entries');
```

## Tips

- For large datasets prefer `state_getKeysPaged` to avoid stress-testing nodes.
- Combine with Bifrost-specific prefixes (e.g., `Omnipool` or `Farming`) to iterate liquidity pools and reward schedules.

---

## state_getKeysPaged - Bifrost RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Bifrost. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_getKeysPaged` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Bifrost
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bifrost/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bifrost/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getMetadata - Bifrost RPC Method

Returns the runtime metadata for Bifrost as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_getMetadata` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Bifrost
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Bifrost RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Bifrost:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Bifrost:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bifrost/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/bifrost/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) - Call runtime APIs described in metadata

---

## state_getRuntimeVersion - Bifrost RPC Method

# state_getRuntimeVersion - Bifrost RPC Method

Returns the runtime version information for Bifrost, including the spec name, spec version, implementation version, and supported API versions.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_getRuntimeVersion` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Bifrost
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Bifrost in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bifrost/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Bifrost RPC Method

Returns the SCALE-encoded storage value for a given key on Bifrost. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_getStorage` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Bifrost
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Bifrost RPC Method
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bifrost/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bifrost/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call runtime APIs for computed state that is not directly in storage
- `state_subscribeStorage` -- Subscribe to storage changes in real time via WebSocket

---

## state_queryStorageAt - Bifrost RPC Method

Queries multiple storage keys at a specific block on Bifrost, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`state_queryStorageAt` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Bifrost
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Bifrost RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bifrost/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bifrost/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/bifrost/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bifrost/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## system_chain - Bifrost RPC Method

Returns the chain name of the Bifrost network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`system_chain` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Network Verification** -- Confirm your application is connected to the correct Bifrost network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Bifrost"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', 'Bifrost');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/bifrost/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bifrost/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bifrost/rpc_methods) -- List all available RPC methods

---

## system_health - Bifrost RPC Method

# system_health - Bifrost RPC Method

Returns the health status of the Bifrost node, including peer count, sync state, and whether the node expects to have peers.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`system_health` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Bifrost
- **Load Balancing** - Route requests only to healthy, fully synced nodes for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Bifrost:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Bifrost nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) - Get chain name
- `system_syncState` - Get detailed sync progress
- `system_peers` - Get detailed peer information

---

## system_name - Bifrost RPC Method

Returns the node implementation name on Bifrost. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`system_name` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Bifrost
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Bifrost node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bifrost node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Bifrost node implementation: {name}')

# system_name - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
name = substrate.rpc_request('system_name', [])['result']
print(f'Bifrost node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bifrost node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Bifrost nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bifrost/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bifrost/rpc_methods) -- List all available RPC methods

---

## system_properties - Bifrost RPC Method

Returns the chain-specific properties for Bifrost, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`system_properties` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Bifrost
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bifrost/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bifrost/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bifrost/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/bifrost/rpc_methods) -- List all available RPC methods

---

## system_version - Bifrost RPC Method

Returns the node implementation version string on Bifrost. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Bifrost?** Build on Polkadot's largest liquid staking appchain with 60% DOT LST market share and $125M+ TVL with first LST governance on OpenGov, 60% DOT market share, Hyperbridge ETH integration, and 500K DOT treasury support.

## When to Use This Method

`system_version` is essential for liquid staking developers, DeFi builders, and teams requiring cross-chain yield solutions:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Bifrost
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for omnichain liquid staking (vDOT, vKSM, vGLMR, vMOVR, vASTR), cross-chain vToken governance, and DOT/ETH liquidity bridging
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Bifrost node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bifrost node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Bifrost node version: {version}')

# system_version - Bifrost RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY')
version = substrate.rpc_request('system_version', [])['result']
print(f'Bifrost node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bifrost-polkadot.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bifrost node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Bifrost nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bifrost/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bifrost/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/bifrost/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bifrost/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/bifrost/rpc_methods) -- List all available RPC methods

---

## Bittensor - Decentralized Machine Learning Network

## Description

Interact with Substrate JSON‑RPC. This method is commonly used to build reliable indexers, developer tooling, and responsive UIs.

## Why Build on Bittensor?

Bittensor is a revolutionary blockchain that creates a decentralized marketplace for machine learning models and computational intelligence. Built on Substrate, Bittensor enables:

### **AI-Native Blockchain**

- **Decentralized ML** - Train and serve models in a trustless environment
- **Incentivized Intelligence** - Earn TAO tokens for contributing compute and models
- **Collaborative Learning** - Models improve through network-wide collaboration

### **Advanced Capabilities**

- **Neural Mining** - Mine TAO tokens by running AI models
- **36+ Subnets** - Specialized networks for different AI tasks (expanding to 1024)
- **Yuma Consensus** - Novel consensus mechanism for ML validation

### **Innovation Platform**

- **Open AI Marketplace** - Access diverse AI models and services
- **Composable Intelligence** - Build complex AI systems from network primitives
- **Research Network** - Contribute to cutting-edge ML research

## Quick Start with Bittensor

Connect to Bittensor in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Python

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Bittensor mainnet
const provider = new JsonRpcProvider(
  'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Bittensor mainnet
const web3 = new Web3(
  'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Bittensor:', chainId);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```python
from web3 import Web3

# Bittensor - Decentralized Machine Learning Network
w3 = Web3(Web3.HTTPProvider(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
))

# Check connection
if w3.is_connected():
    print("Connected to Bittensor")

# Get latest block
latest_block = w3.eth.block_number
print(f"Latest block: {latest_block}")

# Get account balance
balance = w3.eth.get_balance('0x...')
print(f"Balance: {balance} TAO")
```

## Network Information

| Parameter    | Value      | Details         |
| ------------ | ---------- | --------------- |
| Chain ID     | N/A        | Substrate-based |
| Block Time   | 12 seconds | Average         |
| Native Token | TAO        | 21M max supply  |
| Consensus    | Yuma       | ML Validation   |

## API Reference

Bittensor exposes both an Ethereum-compatible JSON-RPC (via Frontier) and native Substrate RPC. Use either or both depending on your integration.

#### Custom RPC Extensions

Bittensor nodes also expose custom RPC namespaces for chain‑specific AI/ML data:

- `subnetInfo_*`: subnet configuration, hyperparameters, metagraphs
- `neuronInfo_*`: neuron and mechagraph data
- `delegateInfo_*`: delegation and delegate details
- `swap_*`: TAO and ALPHA swap simulation helpers

These are implemented by the chain and surfaced over JSON‑RPC; see the runtime and node RPC code for details. Refer to the links in the Resources section below.

## Common Integration Patterns

### **Subnet Interaction**

Connect to Bittensor subnets for specialized AI tasks:

```javascript
// Query subnet information
async function getSubnetInfo(subnetId) {
  const result = await provider.call({
    to: SUBNET_REGISTRY_ADDRESS,
    data: encodeSubnetQuery(subnetId)
  });

  return decodeSubnetInfo(result);
}

// Register as miner
async function registerMiner(subnetId, modelEndpoint) {
  const tx = {
    to: SUBNET_REGISTRY_ADDRESS,
    data: encodeMinerRegistration(subnetId, modelEndpoint),
    value: REGISTRATION_FEE
  };

  const receipt = await signer.sendTransaction(tx);
  return receipt;
}
```

### **TAO Token Operations**

Manage TAO tokens and staking:

```javascript
// Stake TAO tokens
async function stakeTAO(amount, validatorAddress) {
  const stakingContract = new Contract(
    STAKING_CONTRACT_ADDRESS,
    STAKING_ABI,
    signer
  );

  const tx = await stakingContract.stake(
    validatorAddress,
    { value: parseEther(amount) }
  );

  return tx.wait();
}

// Query staking rewards
async function getStakingRewards(address) {
  const rewards = await stakingContract.pendingRewards(address);
  return formatUnits(rewards, 9); // TAO has 9 decimals
}
```

### **Model Validation**

Validate AI model outputs on-chain:

```javascript
// Submit model output for validation
async function submitModelOutput(subnetId, taskId, output) {
  const validationContract = new Contract(
    VALIDATION_CONTRACT_ADDRESS,
    VALIDATION_ABI,
    signer
  );

  const proof = generateProof(output);
  const tx = await validationContract.submitOutput(
    subnetId,
    taskId,
    output,
    proof
  );

  return tx.wait();
}
```

## Performance Best Practices

### 1. **Efficient Querying**

Optimize queries for Bittensor's unique architecture:

```javascript
// Batch subnet queries
async function batchSubnetQueries(subnetIds) {
  const queries = subnetIds.map(id => ({
    to: SUBNET_REGISTRY_ADDRESS,
    data: encodeSubnetQuery(id)
  }));

  const results = await Promise.all(
    queries.map(q => provider.call(q))
  );

  return results.map(decodeSubnetInfo);
}
```

### 2. **Caching Strategy**

Cache frequently accessed subnet and model data:

```javascript
class SubnetCache {
  constructor(ttl = 60000) { // 1 minute TTL
    this.cache = new Map();
    this.ttl = ttl;
  }

  async get(subnetId, fetcher) {
    const key = `subnet_${subnetId}`;
    const cached = this.cache.get(key);

    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }

    const data = await fetcher(subnetId);
    this.cache.set(key, { data, timestamp: Date.now() });
    return data;
  }
}
```

### 3. **Connection Management**

Maintain persistent connections for real-time updates:

```javascript
// WebSocket connection for real-time updates
const wsProvider = new WebSocketProvider(
  'wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Subscribe to subnet events
wsProvider.on('subnet_update', (event) => {
  console.log('Subnet updated:', event);
});
```

## Troubleshooting Common Issues

### Error: "Invalid subnet ID"

Ensure you're using valid subnet identifiers:

```javascript
// Validate subnet ID before operations
async function validateSubnet(subnetId) {
  const exists = await provider.call({
    to: SUBNET_REGISTRY_ADDRESS,
    data: encodeSubnetExists(subnetId)
  });

  if (!exists) {
    throw new Error(`Subnet ${subnetId} does not exist`);
  }

  return true;
}
```

### Error: "Insufficient stake"

Check staking requirements before operations:

```javascript
// Check minimum stake requirement
async function checkStakeRequirement(operation) {
  const required = await getMinimumStake(operation);
  const current = await getUserStake(address);

  if (current < required) {
    throw new Error(`Need ${required - current} more TAO staked`);
  }
}
```

## Resources & Tools

### Official Resources

- [Bittensor Documentation](https://docs.bittensor.com)
- [Taostats Explorer](https://taostats.io)
- [Bittensor GitHub](https://github.com/opentensor/bittensor)

### Developer Tools

- [Bittensor SDK](https://pypi.org/project/bittensor/)
- [Subnet Template](https://github.com/opentensor/subnet-template)

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

***

*Start building on Bittensor with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## account_nextIndex - Bittensor RPC Method

# account_nextIndex - Bittensor RPC Method

Returns the next valid transaction index (nonce) for a given account. This is an alias for `system_accountNextIndex` and considers both finalized state and pending transactions in the transaction pool. Using the correct nonce is essential when constructing signed extrinsics -- an incorrect nonce leads to `BadProof`, `Future`, or `Stale` transaction errors.

## Code Examples

## Request Parameters

- `accountId` (`string, required`): The SS58-encoded address or hex-encoded AccountId (32 bytes) of the account to query.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "account_nextIndex",
  "params": [
    "<accountId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`number, required`): The next valid nonce for the account. This accounts for pending transactions in the pool.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Use Cases

- **Transaction construction** -- Get the correct nonce before signing and submitting extrinsics to Bittensor (transfers, staking, subnet operations).
- **Batch transaction submission** -- When sending multiple transactions in sequence, increment the nonce manually starting from this value.
- **Pool-aware nonce** -- Unlike reading the nonce directly from storage, this method accounts for pending (not yet included) transactions in the pool.

## Notes

- This method is an alias for `system_accountNextIndex`. Both return identical results.
- The returned nonce includes pending transactions in the pool. If those transactions are dropped, the nonce may shift.
- For rapid transaction submission, consider manually incrementing nonces rather than querying between each submission.

## Related Methods

- [`system_accountNextIndex`](https://www.dwellir.com/docs/bittensor/system_accountNextIndex) -- Canonical method (same functionality)
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit a signed extrinsic using the nonce
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- View pending transactions in the pool
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read the on-chain nonce directly from `System.Account` storage (does not include pool)

---

## archive_v1_body - Bittensor RPC Method

# archive_v1_body - Bittensor RPC Method

Returns the body of a block identified by its hash via the new JSON-RPC v2 archive API. The block body contains the ordered list of extrinsics (transactions, inherents, and unsigned calls) included in that block. This method is part of the `archive` namespace introduced in the new JSON-RPC specification and is designed for clients that need efficient historical data access on Bittensor without maintaining a full follow subscription.

## Code Examples

## Request Parameters

- `hash` (`string, required`): The hex-encoded block hash (e.g. `"0x1e8a..."`) whose body to retrieve.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_body",
  "params": [
    "<hash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array | null, required`): Array of hex-encoded SCALE extrinsics included in the block, in execution order. `null` if the block hash cannot be resolved.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Indexers and explorers** -- Retrieve all extrinsics in a historical block for indexing transfers, staking operations, and subnet registrations on Bittensor.
- **Analytics pipelines** -- Extract historical transaction data across block ranges without replaying the chain. Combine with `archive_v1_hashByHeight` to iterate blocks sequentially.
- **Audit tooling** -- Verify the contents of specific blocks during forensic analysis of on-chain activity.
- **Subnet monitoring** -- Examine historical blocks for subnet registration, weight-setting, and staking extrinsics to analyze Bittensor network behavior over time.

## Bittensor Context

On Bittensor, block bodies contain Substrate extrinsics that encode TAO transfers, subnet operations (`register`, `set_weights`, `add_stake`, `remove_stake`), and governance actions. Each extrinsic is SCALE-encoded and must be decoded using the runtime metadata from the same block era.

## Notes

- This method is part of the new JSON-RPC v2 specification and may not be available on all node configurations.
- On public shared RPC endpoints the archive namespace may be disabled or rate-limited.
- For real-time block body access, consider using `chainHead_v1_body` within a follow subscription instead.

## Related Methods

- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get the header for a historical block
- [`archive_v1_call`](https://www.dwellir.com/docs/bittensor/archive_v1_call) -- Execute a runtime call against historical state
- [`archive_v1_hashByHeight`](https://www.dwellir.com/docs/bittensor/archive_v1_hashByHeight) -- Resolve a block height to a hash before querying its body
- [`chainHead_v1_body`](https://www.dwellir.com/docs/bittensor/chainHead_v1_body) -- Get block body within a follow subscription
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) -- Legacy method to retrieve a full block (header + body)

---

## archive_v1_call - Bittensor RPC Method

# archive_v1_call - Bittensor RPC Method

Executes a runtime API call against the state of a specific historical block via the new JSON-RPC v2 archive API. This allows you to invoke any runtime API function (such as `Metadata_metadata`, `AccountNonceApi_account_nonce`, or `TransactionPaymentApi_query_info`) using the state as it existed at a given block, without needing an active follow subscription.

## Code Examples

## Request Parameters

- `hash` (`string, required`): Hex-encoded block hash identifying which historical state to query against.
- `function` (`string, required`): The runtime API function name (e.g. `"Metadata_metadata"`).
- `callParameters` (`string, required`): Hex-encoded SCALE-encoded call parameters. Use `"0x"` when the function takes no arguments.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_call",
  "params": [
    "<hash>",
    "<function>",
    "<callParameters>"
  ],
  "id": 1
}
```

## Response Fields

- `result.success` (`boolean, required`): `true` when the runtime call executed successfully
- `result.value` (`string, required`): Hex-encoded SCALE output returned by the runtime API when `success` is `true`
- `result.error` (`string, required`): Error string returned by the runtime when `success` is `false`

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "result.success": true,
    "result.value": "<value>",
    "result.error": "<value>"
  }
}
```

## Use Cases

- **Historical metadata retrieval** -- Fetch the runtime metadata as it was at a past block to correctly decode storage and extrinsics from that era.
- **Fee estimation on past state** -- Call `TransactionPaymentApi_query_info` against historical state to understand fee structures at a given block.
- **Cross-version analytics** -- Compare runtime API outputs across different blocks to track how Bittensor subnet parameters evolved over time.

## Bittensor Context

On Bittensor, runtime APIs can expose subnet-specific data, neuron metadata, and staking information. By calling these APIs at historical blocks, you can reconstruct how subnet parameters, validator weights, and incentive mechanisms evolved over time -- essential for research and analytics on the decentralized AI network.

## Notes

- This is part of the new JSON-RPC v2 specification. Availability depends on node configuration and the archive namespace being enabled.
- The `callParameters` must be SCALE-encoded. Use a library such as `@polkadot/types` or `parity-scale-codec` to encode them.
- On public shared RPC endpoints, this method may be disabled or rate-limited.

## Related Methods

- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get the block body from the archive
- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get the block header from the archive
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Legacy runtime API call (at latest or specific block)
- [`state_callAt`](https://www.dwellir.com/docs/bittensor/state_callAt) -- Legacy runtime API call at a specific block hash
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Runtime call within a follow subscription

---

## archive_v1_finalizedHeight - Bittensor RPC Method

# archive_v1_finalizedHeight - Bittensor RPC Method

Returns the height (block number) of the highest finalized block known to the archive node. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget. This method provides a quick way to determine how far the finalized chain has progressed without needing to resolve block hashes.

zed block. |

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_finalizedHeight",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`number, required`): The block number of the latest finalized block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Use Cases

- **Indexer checkpoints** -- Determine the safe finalized height before committing indexed data to your database. Only data at or below this height is guaranteed immutable.
- **Sync progress monitoring** -- Compare the finalized height against the latest best block to gauge how far behind finalization is.
- **Data pipeline boundaries** -- Set upper bounds on block ranges for batch processing, ensuring you only process finalized blocks for analytics on Bittensor subnet activity.
- **Safe state queries** -- Use the finalized height to derive a block hash (via `archive_v1_hashByHeight`) and then query storage at that hash, ensuring the state you read cannot be reverted.

## Bittensor Context

Bittensor uses GRANDPA for deterministic finality. Once a block is finalized, all extrinsics in it (TAO transfers, subnet registrations, weight updates, staking operations) are permanently committed. Indexers and analytics tools should only treat data as authoritative after finalization.

## Notes

- This method is part of the new JSON-RPC v2 specification and may not be enabled on all Bittensor nodes.
- The finalized height increases monotonically and should never decrease.
- On public shared RPC endpoints the archive namespace may be disabled or rate-limited.

## Related Methods

- [`archive_v1_hashByHeight`](https://www.dwellir.com/docs/bittensor/archive_v1_hashByHeight) -- Convert the finalized height to a block hash
- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Fetch the header at the finalized height
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) -- Legacy method returning the finalized block hash
- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Follow the chain head including finality notifications

---

## archive_v1_genesisHash - Bittensor RPC Method

# archive_v1_genesisHash - Bittensor RPC Method

Returns the hash of the genesis block (block 0) as reported by the archive node. The genesis hash uniquely identifies a Substrate-based chain and is commonly used to verify that a client is connected to the correct network. On Bittensor mainnet the genesis hash is a fixed value that never changes.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_genesisHash",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded 32-byte blake2b hash of the genesis block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Network verification** -- Confirm you are connected to Bittensor mainnet (and not a testnet or fork) by comparing the genesis hash against a known value.
- **Transaction signing** -- The genesis hash is required when constructing the signed payload for Substrate extrinsics; using the wrong value causes signature verification to fail.
- **Multi-chain applications** -- Identify which chain a connection belongs to in applications that connect to multiple Substrate networks.

## Bittensor Context

The Bittensor mainnet genesis hash uniquely identifies the network. It is embedded in every signed extrinsic's "signed extensions" payload, binding transactions to the correct chain. If you sign a transaction with the wrong genesis hash, it will be rejected with a `BadProof` error. The genesis hash also distinguishes Bittensor mainnet from testnets and local dev chains.

## Notes

- This method is part of the new JSON-RPC v2 specification. It mirrors `chainSpec_v1_genesisHash` and the legacy `chain_getBlockHash(0)`.
- The genesis hash is immutable for a given chain and can be safely cached.
- On public shared RPC endpoints the archive namespace may be disabled.

## Related Methods

- [`chainSpec_v1_genesisHash`](https://www.dwellir.com/docs/bittensor/chainSpec_v1_genesisHash) -- Get genesis hash via the chainSpec namespace
- [`archive_v1_hashByHeight`](https://www.dwellir.com/docs/bittensor/archive_v1_hashByHeight) -- Get the hash for any block height (use height 0 for genesis)
- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get the genesis block header
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) -- Legacy method to get a block hash by number

---

## archive_v1_hashByHeight - Bittensor RPC Method

# archive_v1_hashByHeight - Bittensor RPC Method

Returns the block hash for a given block height (number) via the new JSON-RPC v2 archive API. Because Substrate chains can have forks, a single height may correspond to more than one block hash on non-finalized parts of the chain. For finalized blocks there is always exactly one hash per height.

zed blocks; may contain multiple for non-finalized forks. Returns an empty array if the height is beyond the chain tip. |

## Code Examples

## Request Parameters

- `height` (`number, required`): The block number (height) to look up.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_hashByHeight",
  "params": [
    "<height>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of hex-encoded block hashes at the given height. Typically one element for finalized blocks; may contain multiple for non-finalized forks. Returns an empty array if the height is beyond the chain tip.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Block resolution** -- Resolve a block number to a hash before querying `archive_v1_body`, `archive_v1_header`, or `archive_v1_call`.
- **Sequential block scanning** -- Iterate through blocks by height for indexing pipelines, converting each height to a hash before fetching block contents.
- **Fork detection** -- Identify forks by checking whether a height returns multiple hashes on the non-finalized chain.

## Notes

- This method is part of the new JSON-RPC v2 specification and may not be available on all node configurations.
- Heights beyond the finalized tip may return multiple hashes due to chain forks.
- On public shared RPC endpoints the archive namespace may be disabled or rate-limited.

## Related Methods

- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get the header once you have a block hash
- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get the block body once you have a block hash
- [`archive_v1_finalizedHeight`](https://www.dwellir.com/docs/bittensor/archive_v1_finalizedHeight) -- Get the latest finalized height
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) -- Legacy method to resolve block number to hash

---

## archive_v1_header - Bittensor RPC Method

# archive_v1_header - Bittensor RPC Method

Returns the header of a block identified by its hash via the new JSON-RPC v2 archive API. The header contains the parent hash, block number, state root, extrinsics root, and digest (which includes consensus-related log items such as BABE slot info and GRANDPA authority changes). This is a lightweight way to inspect block metadata without fetching the full body.

## Code Examples

## Request Parameters

- `hash` (`string, required`): Hex-encoded block hash to retrieve the header for.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_header",
  "params": [
    "<hash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string | null, required`): Hex-encoded SCALE-encoded Substrate header for the requested block. `null` if the block hash cannot be resolved.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Chain traversal** -- Walk the chain backwards by following `parentHash` links from header to header.
- **Light client verification** -- Verify block headers and state roots without downloading the full block body.
- **Indexer metadata** -- Extract block numbers, timestamps (from digest), and state roots for indexed block records.

## Notes

- This method is part of the new JSON-RPC v2 specification and may not be enabled on all Bittensor nodes.
- The header is significantly smaller than a full block, making it ideal for lightweight monitoring.
- On public shared RPC endpoints the archive namespace may be disabled or rate-limited.

## Related Methods

- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get the block body (extrinsics) for the same hash
- [`archive_v1_hashByHeight`](https://www.dwellir.com/docs/bittensor/archive_v1_hashByHeight) -- Resolve a block height to a hash
- [`archive_v1_call`](https://www.dwellir.com/docs/bittensor/archive_v1_call) -- Execute a runtime call at the same block's state
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) -- Legacy method to fetch a block header
- [`chainHead_v1_header`](https://www.dwellir.com/docs/bittensor/chainHead_v1_header) -- Get header within a follow subscription

---

## archive_v1_stopStorage - Bittensor RPC Method

# archive_v1_stopStorage - Bittensor RPC Method

Stops an active storage query operation that was previously started via the archive API. When you initiate a storage query through the archive namespace over a WebSocket connection, the node begins streaming results. Call this method to cancel that operation before it completes, freeing server-side resources.

## Code Examples

## Request Parameters

- `subscriptionId` (`string, required`): The subscription or operation ID returned when the storage query was started.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_stopStorage",
  "params": [
    "<subscriptionId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`boolean, required`): Indicates whether the storage operation was successfully stopped on the shared endpoint.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Resource cleanup** -- Stop a long-running storage stream when you have received enough data, preventing unnecessary load on the node.
- **Error recovery** -- Cancel a stalled or slow storage query and retry with different parameters.
- **Graceful shutdown** -- Clean up active subscriptions before disconnecting your WebSocket client.

## Notes

- This method is part of the new JSON-RPC v2 specification and operates over WebSocket connections.
- Always stop subscriptions you no longer need to avoid leaking server resources.
- On public shared RPC endpoints the archive namespace may be disabled.

## Related Methods

- [`archive_v1_storageDiff`](https://www.dwellir.com/docs/bittensor/archive_v1_storageDiff) -- Stream storage changes between blocks (stopped with `archive_v1_storageDiff_stopStorageDiff`)
- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get block body from the archive
- [`chainHead_v1_stopOperation`](https://www.dwellir.com/docs/bittensor/chainHead_v1_stopOperation) -- Stop a follow-based operation
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop following the chain head

---

## archive_v1_storage - JSON-RPC Method

# archive_v1_storage - JSON-RPC Method

## Description

Query historical blocks, storage, and diffs via the archive service. Build fast historical analytics without re‑executing the chain.

Starts an archive storage operation for the requested block hash and storage queries.
The call returns an opaque operation ID immediately, then streams matching
`archive_v1_storageEvent` notifications over the same WebSocket connection.

## Code Examples

## Request Parameters

- `hash` (`string, required`): Hex-encoded block hash whose archived storage you want to inspect.
- `items` (`array<object>, required`): One or more storage queries describing the key and lookup type to fetch from archive storage.
- `childTrie` (`string | null, optional`): Child trie key for default-namespace child storage lookups. Use `null` for main storage.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_storage",
  "params": [
    "<blockHash>",
    [
      {
        "key": "0x3a636f6465",
        "type": "value"
      }
    ],
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Opaque operation ID. Read the requested storage items from subsequent `archive_v1_storageEvent` notifications on the same connection.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<operationId>"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

---

## archive_v1_storageDiff - Bittensor RPC Method

# archive_v1_storageDiff - Bittensor RPC Method

Streams the differences in storage between two blocks via the new JSON-RPC v2 archive API. This method reports which storage keys were added, modified, or deleted between a previous block and a target block. It is designed for efficient state synchronization and change tracking without downloading full storage snapshots.

## Code Examples

## Request Parameters

- `hash` (`string, required`): Hex-encoded hash of the target block (the newer block).
- `items` (`array, required`): Array of storage query items specifying which keys or prefixes to watch for changes.
- `previousHash` (`string, optional`): Hex-encoded hash of the previous block to diff against. Defaults to the parent block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_storageDiff",
  "params": [
    "<hash>",
    "<items>",
    "<previousHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`OBJECT, required`): The direct RPC response is a subscription/operation ID string. Results then arrive as `archive_v1_storageDiffEvent` notifications over the same WebSocket connection: | Event | Description | |-------|-------------| | `storageDiff` | A batch of changed keys and returned values or hashes | | `storageDiffError` | An error was encountered while streaming diff results | | `storageDiffDone` | The diff stream has completed |

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Incremental indexing** -- Detect only the storage keys that changed between blocks, dramatically reducing the amount of data an indexer needs to process.
- **State sync** -- Efficiently synchronize a local state mirror by applying only the diffs instead of re-reading entire storage.
- **Change auditing** -- Track changes to specific storage maps (e.g. Bittensor subnet parameters, staking ledgers) between two points in time.

## Notes

- This method is part of the new JSON-RPC v2 specification and requires a WebSocket connection for streaming results.
- Use `archive_v1_storageDiff_stopStorageDiff` to cancel an in-progress diff operation.
- On public shared RPC endpoints the archive namespace may be disabled or rate-limited.

## Related Methods

- [`archive_v1_storageDiff_stopStorageDiff`](https://www.dwellir.com/docs/bittensor/archive_v1_storageDiff_stopStorageDiff) -- Stop an active storage diff stream
- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get the block body for context around changes
- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get the block header for the diffed blocks
- [`state_queryStorage`](https://www.dwellir.com/docs/bittensor/state_queryStorage) -- Legacy method to query storage changes over a block range
- [`chainHead_v1_storage`](https://www.dwellir.com/docs/bittensor/chainHead_v1_storage) -- Query storage within a follow subscription

---

## archive_v1_storageDiff_stopStorageDiff - Bittensor RPC Method

# archive_v1_storageDiff_stopStorageDiff - Bittensor RPC Method

Stops an active storage diff operation that was previously started with `archive_v1_storageDiff`. When a storage diff is streaming results over WebSocket, this method cancels the operation and frees server-side resources. Always call this when you no longer need the diff results to avoid unnecessary load on the node.

## Code Examples

## Request Parameters

- `subscriptionId` (`string, required`): The subscription or operation ID returned when the storage diff was started.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "archive_v1_storageDiff_stopStorageDiff",
  "params": [
    "<subscriptionId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`boolean, required`): Indicates whether the storage diff operation was successfully stopped on the shared endpoint.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Resource cleanup** -- Cancel a long-running diff operation once you have collected the changes you need.
- **Error recovery** -- Stop a stalled diff and retry with adjusted parameters (e.g. a narrower key prefix or smaller block range).
- **Graceful shutdown** -- Clean up all active diff subscriptions before disconnecting your WebSocket client.

## Notes

- This method is part of the new JSON-RPC v2 specification and operates over WebSocket connections.
- Always stop diff subscriptions you no longer need. Leaked subscriptions consume memory and bandwidth on the server.
- On public shared RPC endpoints the archive namespace may be disabled.

## Related Methods

- [`archive_v1_storageDiff`](https://www.dwellir.com/docs/bittensor/archive_v1_storageDiff) -- Start a storage diff stream between two blocks
- [`archive_v1_stopStorage`](https://www.dwellir.com/docs/bittensor/archive_v1_stopStorage) -- Stop a regular storage query operation
- [`chainHead_v1_stopOperation`](https://www.dwellir.com/docs/bittensor/chainHead_v1_stopOperation) -- Stop a follow-based operation
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop following the chain head entirely

---

## author_hasKey - Bittensor RPC Method

# author_hasKey - Bittensor RPC Method

Checks whether the node's keystore contains a given public key for a specified key type. This is an administrative method used to verify that a validator or collator node has the correct session keys loaded before participating in block production or consensus. It is typically disabled on public shared RPC endpoints.

## Code Examples

## Request Parameters

- `publicKey` (`string, required`): Hex-encoded public key to look up (e.g. `"0xd43593..."`)
- `keyType` (`string, required`): Four-character key type identifier (e.g. `"babe"`, `"gran"`, `"aura"`, `"imon"`)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_hasKey",
  "params": [
    "<publicKey>",
    "<keyType>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`boolean, required`): `true` if the key exists in the keystore for the given type, `false` otherwise.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Validator setup verification** -- Confirm that session keys are properly loaded in the keystore before a validator begins producing blocks.
- **Key rotation checks** -- After rotating session keys with `author_rotateKeys`, verify the new keys are present.
- **Monitoring and alerts** -- Automated health checks to ensure a validator node still has the required keys.

## Notes

- This is an unsafe/administrative RPC method. It must be explicitly enabled on the node with the `--rpc-methods unsafe` flag.
- On Dwellir's shared public RPC endpoints, expect this method to return an error.
- The key type is a 4-byte ASCII identifier corresponding to the consensus or session module that uses the key.

## Related Methods

- [`author_rotateKeys`](https://www.dwellir.com/docs/bittensor/author_rotateKeys) -- Generate new session keys in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit extrinsics (requires keys for signing)
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Check what roles the node is configured with (authority, full, etc.)

---

## author_pendingExtrinsics - Bittensor RPC Method

Returns all pending extrinsics currently in the transaction pool on Bittensor. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`author_pendingExtrinsics` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Bittensor
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Bittensor transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bittensor/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Bittensor RPC Method

Generate a new set of session keys on Bittensor. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`author_rotateKeys` is critical for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Validator Setup** - Generate initial session keys when setting up a new validator on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Bittensor RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Bittensor:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Bittensor RPC Method

Submits a signed extrinsic to Bittensor and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Bittensor
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Bittensor RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- [`system_accountNextIndex`](https://www.dwellir.com/docs/bittensor/system_accountNextIndex) -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bittensor/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Bittensor RPC Method

Submits a fully signed extrinsic to Bittensor for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`author_submitExtrinsic` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Token Transfers** -- Send native tokens or assets between accounts on Bittensor
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Bittensor RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/bittensor/payment_queryInfo) -- Estimate fees before submitting
- [`system_accountNextIndex`](https://www.dwellir.com/docs/bittensor/system_accountNextIndex) -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Bittensor RPC Method

Returns the block hash of the latest BEEFY-finalized block on Bittensor. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`beefy_getFinalizedHead` is not currently exposed on Dwellir's public Bittensor RPC. Use finalized-block signals from supported chain-RPC methods instead.

- **Finalized head reads** — Use `chain_getFinalizedHead` for the current finalized block hash
- **Realtime finalized updates** — Subscribe to `chain_subscribeFinalizedHeads` for push updates

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'

# beefy_getFinalizedHead - Bittensor RPC Method
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32601, "message": "Method not found" }
}
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  const result = await api.rpc.beefy.getFinalizedHead();
  console.log('Unexpected result:', result.toString());
} catch (error) {
  console.log('beefy_getFinalizedHead unsupported:', error.message);
}

await api.disconnect();
```

```python
import requests

response = requests.post(
  'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
  json={
    'jsonrpc': '2.0',
    'method': 'beefy_getFinalizedHead',
    'params': [],
    'id': 1,
  },
)

print(response.json())
# {'error': {'code': -32601, 'message': 'Method not found'}}
```

## Alternative Monitoring Patterns on Bittensor

Because Bittensor does not expose `beefy_getFinalizedHead` on this shared surface, use these finalized-block signals instead.

### 1. Check latest finalized hash directly

Use `chain_getFinalizedHead` for a simple on-demand read of finalized head:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://...');
const api = await ApiPromise.create({ provider });

const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block:', finalizedHash.toHex());
```

### 2. Stream finalized heads

Use `chain_subscribeFinalizedHeads` when you need push-based finalized updates for downstream systems.

```javascript
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number.toString()}: ${header.hash.toHex()}`);
});
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/bittensor/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Bittensor RPC Method

Retrieves complete block information from Bittensor, including the block header, extrinsics, and justifications.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x2f0555cc76fc2840a25a6f3a0f0e6d0b1a6dd2e0cecc9e4c2e9e6f3a8d2e5c1b"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Bittensor RPC Method
curl https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x2f0555cc76fc2840a25a6f3a0f0e6d0b1a6dd2e0cecc9e4c2e9e6f3a8d2e5c1b"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x2f0555cc76fc2840a25a6f3a0f0e6d0b1a6dd2e0cecc9e4c2e9e6f3a8d2e5c1b';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x2f0555cc76fc2840a25a6f3a0f0e6d0b1a6dd2e0cecc9e4c2e9e6f3a8d2e5c1b')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Bittensor RPC Method

Returns the block hash for a given block number on Bittensor. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`chain_getBlockHash` is fundamental for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Bittensor
- **Block Navigation** - Navigate the blockchain history for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Bittensor
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Bittensor for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Bittensor state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Bittensor network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Bittensor RPC Method

Returns the hash of the last finalized block on Bittensor. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`chain_getFinalizedHead` is critical for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Bittensor
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Bittensor RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Bittensor:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/bittensor/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHead - JSON-RPC Method

# chain_getHead - JSON-RPC Method

## Description

Return the best (non-finalized) block hash. Use for low‑latency reads when finality is not required.

Returns the block hash of the current best block (not necessarily finalized).

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded best-block hash from the current non-finalized head.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x7d3f644499bb7478ff9e1ae7eab08eb17426d59ebceebbeadcceabd5f985a15e"
}
```

---

## chain_getHeader - Bittensor RPC Method

Returns the block header for a given hash on Bittensor. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`chain_getHeader` is ideal for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Bittensor
- **Chain Synchronization** - Track block production and monitor chain progress for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Bittensor:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Bittensor chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Bittensor block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_getRuntimeVersion - JSON-RPC Method

# chain_getRuntimeVersion - JSON-RPC Method

Returns the current runtime specification and transaction version for the node. As a developer, you use this to:

- Detect runtime upgrades and adjust clients accordingly (e.g., refresh metadata).
- Ensure your transaction building/signing logic matches the node’s transactionVersion.
- Gate feature flags or migrations based on specName/specVersion (e.g., API changes between releases).

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`string, required`): Runtime specification name.
- `implName` (`string, required`): Runtime implementation name.
- `authoringVersion` (`number, required`): Authoring version used for block production.
- `specVersion` (`number, required`): Runtime specification version.
- `implVersion` (`number, required`): Implementation version for the node runtime.
- `transactionVersion` (`number, optional`): Transaction format version used when signing extrinsics.
- `stateVersion` (`number, optional`): State trie version exposed by the runtime.
- `apis` (`array, required`): Supported runtime API IDs with their versions.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "node-subtensor",
    "implName": "node-subtensor",
    "authoringVersion": 1,
    "specVersion": 377,
    "implVersion": 1,
    "transactionVersion": 0,
    "stateVersion": 1,
    "apis": [
      [
        "0xdf6acb689907609b",
        5
      ]
    ]
  }
}
```

---

## chain_subscribeAllHeads - JSON-RPC Method

# chain_subscribeAllHeads - JSON-RPC Method

Subscribes to every imported block header over a WebSocket connection, including non-finalized fork heads. This is useful when you need the earliest possible view of block production rather than only finalized chain state.

The initial JSON-RPC response returns a subscription ID. Header payloads are delivered afterward as WebSocket notifications for that subscription.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_subscribeAllHeads",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`string, required`): Subscription ID returned by the node. Subsequent WebSocket notifications carry imported header payloads for this subscription.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_subscribeAllHeads",
    "params": [],
    "id": 1
  }'
```

### JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const api = await ApiPromise.create({
  provider: new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
});

const unsub = await api.rpc.chain.subscribeAllHeads((header) => {
  console.log(`Imported block #${header.number}`);
  console.log(`Hash: ${header.hash?.toHex?.() ?? 'notification header'}`);
});

// Later: unsub();
```

## Use Cases

- **Fork-aware indexers** -- Process every imported head, including temporary forks, before finalization settles.
- **Latency-sensitive monitoring** -- React to new blocks as soon as they are imported by the node.
- **Chain analytics** -- Observe short-lived fork activity or competing block candidates during periods of network churn.

## Notes

- This subscription emits more updates than `chain_subscribeNewHeads` because it includes non-finalized heads.
- Cancel the subscription with `chain_unsubscribeAllHeads` when you no longer need updates.
- Use `chain_subscribeFinalizedHeads` if your workflow only cares about finalized blocks.

## Related Methods

- [`chain_unsubscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeAllHeads) -- Cancel this subscription
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Subscribe to canonical new heads
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) -- Subscribe to finalized heads only

---

## chain_subscribeFinalizedHeads - Bittensor RPC Method

Subscribe to receive notifications when blocks are finalized on Bittensor. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Exchange Deposits** - Only credit funds after finalization for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Bittensor RPC Method
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Bittensor:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/bittensor/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Bittensor RPC Method

Subscribe to receive notifications when new block headers are produced on Bittensor. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`chain_subscribeNewHeads` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Block Monitoring** - Track new blocks in real time on Bittensor for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Bittensor RPC Method
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Bittensor as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Bittensor:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) - Get full block data with extrinsics
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) - Unsubscribe from new heads

---

## chain_subscribeRuntimeVersion - Bittensor RPC Method

# chain_subscribeRuntimeVersion - Bittensor RPC Method

Subscribes to runtime version changes over a WebSocket connection. Each time the Bittensor runtime is upgraded, this subscription emits a notification containing the new runtime version details. This allows applications to detect upgrades in real time and update their type registries, metadata, and decoders accordingly.

The initial JSON-RPC response returns a subscription ID. Runtime version objects are delivered afterward as WebSocket notifications on that subscription.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_subscribeRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `specName` (`string, required`): Runtime specification name (e.g. `"node-subtensor"`)
- `implName` (`string, required`): Implementation name
- `specVersion` (`number, required`): Specification version number; increments with each runtime upgrade
- `implVersion` (`number, required`): Implementation version
- `transactionVersion` (`number, required`): Transaction format version; changes indicate breaking extrinsic format changes
- `apis` (`array, required`): List of supported runtime API versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_subscribeRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

### JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const api = await ApiPromise.create({
  provider: new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
});

const unsub = await api.rpc.chain.subscribeRuntimeVersion((version) => {
  console.log(`Runtime upgrade detected: specVersion=${version.specVersion}`);
  console.log(`Transaction version: ${version.transactionVersion}`);
});

// Later: unsub();
```

## Use Cases

- **Runtime upgrade detection** -- React to runtime upgrades by refreshing metadata and type registries so your application can decode new extrinsic and storage formats.
- **Indexer maintenance** -- Pause or restart indexing pipelines when a runtime upgrade changes storage layouts or extrinsic formats.
- **Dashboard alerts** -- Display notifications to operators when the Bittensor runtime version changes.

## Notes

- The first notification is emitted immediately with the current runtime version.
- Cancel the subscription with `chain_unsubscribeRuntimeVersion` when no longer needed.
- The `transactionVersion` field is critical: if it changes, previously constructed unsigned extrinsics may no longer be valid.

## Related Methods

- [`chain_unsubscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeRuntimeVersion) -- Cancel this subscription
- [`state_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_subscribeRuntimeVersion) -- Equivalent subscription via the state namespace
- [`chain_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_getRuntimeVersion) -- One-shot query for current runtime version
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get the full runtime metadata after detecting an upgrade

---

## chain_unsubscribeAllHeads - Bittensor RPC Method

# chain_unsubscribeAllHeads - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `chain_subscribeAllHeads`. Provide the numeric subscription ID that was returned when the subscription was created. After calling this method, no further block header notifications will be delivered for that subscription.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `chain_subscribeAllHeads`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeAllHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the subscription ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeAllHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Subscription cleanup** -- Cancel the all-heads subscription when your application no longer needs to track every imported block header.
- **Resource management** -- Prevent server-side resource leaks by unsubscribing before disconnecting from the WebSocket.
- **Subscription rotation** -- Stop an existing subscription before starting a new one with different parameters.

## Notes

- Always unsubscribe when you are done to free server-side resources. This is especially important for long-running applications.
- If the WebSocket connection is dropped, the server automatically cleans up subscriptions, but explicit unsubscription is still best practice.
- This is a WebSocket-only method; it cannot be called over HTTP.
- In Dwellir-hosted Bittensor environments, this call path currently returns `-32603 Internal error` when called directly; close and re-establish the WebSocket session to clear stale subscriptions when needed.

## Related Methods

- [`chain_subscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeAllHeads) -- Start the subscription this method cancels
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Cancel a best-block header subscription
- [`chain_unsubscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalizedHeads) -- Cancel a finalized header subscription
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop a v2 chain head follow subscription

---

## chain_unsubscribeFinalisedHeads - Bittensor RPC Method

# chain_unsubscribeFinalisedHeads - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `chain_subscribeFinalisedHeads` (British spelling). This is a legacy alias for `chain_unsubscribeFinalizedHeads` and is functionally identical. Provide the subscription ID returned when the subscription was created.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by the subscribe call.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeFinalisedHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeFinalisedHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Legacy client support** -- Used by older clients or libraries that use British spelling (`Finalised` vs `Finalized`).
- **Subscription cleanup** -- Cancel a finalized-heads subscription when you no longer need finality notifications.

## Notes

- This is a British spelling alias. Modern clients should use `chain_unsubscribeFinalizedHeads`.
- Always unsubscribe when done to prevent server-side resource leaks.
- WebSocket-only; cannot be called over HTTP.

## Related Methods

- [`chain_unsubscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalizedHeads) -- Canonical (American spelling) form
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) -- Start a finalized-heads subscription
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Cancel a best-block header subscription
- [`chain_unsubscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeAllHeads) -- Cancel an all-heads subscription

---

## chain_unsubscribeFinalizedHeads - Bittensor RPC Method

# chain_unsubscribeFinalizedHeads - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `chain_subscribeFinalizedHeads`. After calling this method, no further finalized block header notifications will be delivered for that subscription. Provide the numeric subscription ID that was returned when the subscription was created.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `chain_subscribeFinalizedHeads`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeFinalizedHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeFinalizedHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Subscription cleanup** -- Cancel the finalized-heads subscription when your application (indexer, dashboard, alerting system) no longer needs finality notifications.
- **Resource management** -- Prevent server-side resource leaks in long-running WebSocket applications.
- **Subscription rotation** -- Stop one subscription before starting a fresh one after reconnection or reconfiguration.

## Notes

- Always unsubscribe when done. Even though the server cleans up on disconnect, explicit unsubscription is best practice.
- The British spelling alias `chain_unsubscribeFinalisedHeads` is also available and functionally identical.
- WebSocket-only; cannot be called over HTTP.

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) -- Start the finalized-heads subscription
- [`chain_unsubscribeFinalisedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalisedHeads) -- British spelling alias
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Cancel a best-block header subscription
- [`chain_unsubscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeAllHeads) -- Cancel an all-heads subscription
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop a v2 chain head follow subscription

---

## chain_unsubscribeNewHead - Bittensor RPC Method

# chain_unsubscribeNewHead - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `chain_subscribeNewHead` (singular form, a legacy alias). This is functionally identical to `chain_unsubscribeNewHeads` and exists for backward compatibility. Provide the subscription ID returned when the subscription was created.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by the subscribe call.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeNewHead",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeNewHead",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Legacy client support** -- Used by older clients or libraries that reference the singular form of the method name.
- **Subscription cleanup** -- Cancel a new-head subscription when you no longer need real-time block notifications.

## Notes

- This is a legacy alias. Modern clients should use `chain_unsubscribeNewHeads` (plural).
- Always unsubscribe when you are done to free server-side resources.
- WebSocket-only; cannot be called over HTTP.

## Related Methods

- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Canonical (plural) form of this method
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Start a new-head subscription
- [`chain_unsubscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeAllHeads) -- Cancel an all-heads subscription
- [`chain_unsubscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalizedHeads) -- Cancel a finalized-heads subscription

---

## chain_unsubscribeNewHeads - JSON-RPC Method

# chain_unsubscribeNewHeads - JSON-RPC Method

Cancels a WebSocket subscription that was created with `chain_subscribeNewHeads`. Pass the subscription ID you received from the original call.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `chain_subscribeNewHeads`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeNewHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the subscription ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeNewHeads",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Subscription cleanup** -- Stop receiving new-head notifications when a consumer shuts down.
- **Resource management** -- Release server-side subscription state in long-running dashboards or indexers.

## Notes

- Use the subscription ID returned by `chain_subscribeNewHeads`.
- Valid active subscription IDs return `true`. Invalid or already-closed IDs return `false`.
- This method is only available over WebSocket.
- In Dwellir-hosted Bittensor environments, unsubscribing this way can return `-32603 Internal error` after a successful subscription flow test; if cleanup is required, close the WebSocket connection to release session state.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Start a new-head subscription
- [`chain_unsubscribeNewHead`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHead) -- Legacy singular alias
- [`chain_unsubscribeAllHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeAllHeads) -- Cancel an all-heads subscription

---

## chain_unsubscribeRuntimeVersion - JSON-RPC...

# chain_unsubscribeRuntimeVersion - JSON-RPC...

Cancels a runtime-version subscription that was created with `chain_subscribeRuntimeVersion`. Provide the subscription ID returned by the original subscription call.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `chain_subscribeRuntimeVersion`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeRuntimeVersion",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the subscription ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "chain_unsubscribeRuntimeVersion",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Upgrade monitor cleanup** -- Stop a runtime-version watcher once your process no longer needs upgrade notifications.
- **Controlled reconnects** -- Replace an existing subscription cleanly before creating a new one.

## Notes

- Use the subscription ID from `chain_subscribeRuntimeVersion`.
- A successful response returns `true`.
- This method is only available over WebSocket.

## Related Methods

- [`chain_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_subscribeRuntimeVersion) -- Start the runtime-version subscription
- [`state_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_subscribeRuntimeVersion) -- Equivalent subscription via the state namespace

---

## chainHead_v1_body - Bittensor RPC Method

# chainHead_v1_body - Bittensor RPC Method

Retrieves the block body (list of extrinsics) for a block that is part of an active `chainHead_v1_follow` subscription. The block must have been reported by the follow subscription -- you cannot query arbitrary historical blocks. This method is part of the new JSON-RPC v2 chain head specification designed for efficient real-time chain tracking.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID returned by `chainHead_v1_follow`.
- `hash` (`string, required`): Hex-encoded block hash. Must be a block reported by the follow subscription.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_body",
  "params": [
    "<followSubscription>",
    "<hash>"
  ],
  "id": 1
}
```

## Response Fields

- `result.result` (`"started"` or `"limitReached", required`): `"started"` includes an `operationId`; `"limitReached"` means the node refused to start another operation
- `result.operationId` (`string, required`): Operation ID used to correlate follow-up events when the response is `"started"`

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "result.result": "<value>",
    "result.operationId": "<value>"
  }
}
```

## Use Cases

- **Real-time indexing** -- Fetch block bodies as new blocks arrive to index Bittensor extrinsics in real-time.
- **Transaction monitoring** -- Inspect the extrinsics in each new block to detect specific transactions (transfers, subnet registrations, staking operations).
- **Block explorer backends** -- Serve block body data to users as new blocks are produced.

## Notes

- Requires an active `chainHead_v1_follow` subscription. The block hash must be pinned (reported) by that subscription.
- This method is experimental and may not be enabled on public shared RPC endpoints.
- Use `chainHead_v1_stopOperation` to cancel an in-progress body fetch.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start following the chain head (required before using this method)
- [`chainHead_v1_header`](https://www.dwellir.com/docs/bittensor/chainHead_v1_header) -- Get the header for a followed block
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Execute a runtime call on a followed block
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop the follow subscription
- [`archive_v1_body`](https://www.dwellir.com/docs/bittensor/archive_v1_body) -- Get block body for arbitrary historical blocks
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) -- Legacy method to retrieve a full block

---

## chainHead_v1_call - Bittensor RPC Method

# chainHead_v1_call - Bittensor RPC Method

Executes a runtime API function against the state of a block that is pinned by an active `chainHead_v1_follow` subscription. This allows you to call any runtime API (such as `Metadata_metadata`, `AccountNonceApi_account_nonce`, or custom Bittensor runtime APIs) at a specific block known to the follow subscription.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID from `chainHead_v1_follow`.
- `hash` (`string, required`): Hex-encoded block hash. Must be pinned by the follow subscription.
- `function` (`string, required`): Runtime API function name (e.g. `"Metadata_metadata"`).
- `callParameters` (`string, required`): Hex-encoded SCALE-encoded input parameters. Use `"0x"` for no arguments.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_call",
  "params": [
    "<followSubscription>",
    "<hash>",
    "<function>",
    "<callParameters>"
  ],
  "id": 1
}
```

## Response Fields

- `result.result` (`"started"` or `"limitReached", required`): `"started"` includes an `operationId`; `"limitReached"` means the node refused to start another operation
- `result.operationId` (`string, required`): Operation ID used to correlate follow-up events when the response is `"started"`

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "result.result": "<value>",
    "result.operationId": "<value>"
  }
}
```

## Use Cases

- **Live metadata tracking** -- Fetch runtime metadata at each new block to detect runtime upgrades on Bittensor in real time.
- **Nonce resolution** -- Call `AccountNonceApi_account_nonce` at the latest block to get the correct nonce for transaction submission.
- **Custom runtime queries** -- Invoke Bittensor-specific runtime APIs to query subnet information, neuron data, or stake details at a known block.

## Notes

- Requires an active `chainHead_v1_follow` subscription with the block hash pinned.
- The `callParameters` must be SCALE-encoded. Use a codec library to encode them.
- This method is experimental and may not be enabled on public shared RPC endpoints.
- Use `chainHead_v1_stopOperation` to cancel an in-progress call.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start the follow subscription (required first)
- [`chainHead_v1_storage`](https://www.dwellir.com/docs/bittensor/chainHead_v1_storage) -- Query storage on a followed block
- [`chainHead_v1_header`](https://www.dwellir.com/docs/bittensor/chainHead_v1_header) -- Get header for a followed block
- [`archive_v1_call`](https://www.dwellir.com/docs/bittensor/archive_v1_call) -- Runtime call on historical archive blocks
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Legacy runtime API call method

---

## chainHead_v1_continue - Bittensor RPC Method

# chainHead_v1_continue - Bittensor RPC Method

Resumes a paused operation within an active `chainHead_v1_follow` subscription. Some operations (like storage queries returning large result sets) are delivered in pages. When the node pauses to apply backpressure, it sends an `operationWaitingForContinue` event. You must call this method to receive the next page of results.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID from `chainHead_v1_follow`.
- `operationId` (`string, required`): The operation ID from the paused operation's `operationWaitingForContinue` event.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_continue",
  "params": [
    "<followSubscription>",
    "<operationId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`null, required`): Acknowledges the continue request. The next page of results will be delivered as a follow subscription notification.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Large storage queries** -- When querying many storage keys via `chainHead_v1_storage`, the node may pause after delivering a batch. Call `continue` to receive the next batch.
- **Backpressure handling** -- Implement proper flow control in your client by processing each batch before requesting the next one with `continue`.
- **Reliable data streaming** -- Ensure no data is lost by acknowledging each page before the node sends the next.

## Notes

- This method is part of the new JSON-RPC v2 specification and works only within a follow subscription context.
- Only call `continue` after receiving an `operationWaitingForContinue` event. Calling it at other times will return an error.
- This method is experimental and may not be available on public shared RPC endpoints.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start the follow subscription
- [`chainHead_v1_storage`](https://www.dwellir.com/docs/bittensor/chainHead_v1_storage) -- Storage queries that may require pagination via continue
- [`chainHead_v1_stopOperation`](https://www.dwellir.com/docs/bittensor/chainHead_v1_stopOperation) -- Cancel an operation instead of continuing it
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop the entire follow subscription

---

## chainHead_v1_follow - Bittensor RPC Method

# chainHead_v1_follow - Bittensor RPC Method

Starts a follow subscription that tracks the chain head in real time over a WebSocket connection. The subscription emits events as new blocks are produced, finalized, or pruned. Blocks reported by the subscription are "pinned" and can be queried with `chainHead_v1_header`, `chainHead_v1_body`, `chainHead_v1_call`, and `chainHead_v1_storage`. This is the entry point for the new JSON-RPC v2 chain head API.

zed`| Emitted once with`finalizedBlockHashes`and, when requested, runtime info. |
|`newBlock`| A new block has been imported. Contains the block hash, parent hash, and optional runtime info. |
|`bestBlockChanged`| The best (most recent non-finalized) block has changed. |
|`finalized`| One or more blocks have been finalized. Contains arrays of finalized and pruned block hashes. |
|`stop\` | The subscription has been terminated by the server (e.g. too many pinned blocks). |

## Code Examples

## Request Parameters

- `withRuntime` (`boolean, required`): If `true`, the subscription includes runtime information (spec version, APIs) in `newBlock` events. Set to `true` if you need to detect runtime upgrades.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_follow",
  "params": [
    "<withRuntime>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`OBJECT, required`): Returns a subscription ID and begins streaming events: | Event | Description | |-------|-------------| | `initialized` | Emitted once with `finalizedBlockHashes` and, when requested, runtime info. | | `newBlock` | A new block has been imported. Contains the block hash, parent hash, and optional runtime info. | | `bestBlockChanged` | The best (most recent non-finalized) block has changed. | | `finalized` | One or more blocks have been finalized. Contains arrays of finalized and pruned block hashes. | | `stop` | The subscription has been terminated by the server (e.g. too many pinned blocks). |

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Real-time block processing** -- Build indexers or explorers that react to new Bittensor blocks as they arrive, without polling.
- **Finality tracking** -- Know exactly when blocks are finalized by GRANDPA, enabling safe database commits and user confirmations.
- **Runtime upgrade detection** -- With `withRuntime: true`, detect spec version changes immediately to update your type registry and decoders.
- **Fork awareness** -- The `finalized` event reports pruned blocks, allowing your application to handle chain reorganizations correctly.

## Notes

- This method is the foundation of the new JSON-RPC v2 chain head API. All other `chainHead_v1_*` methods require an active follow subscription.
- The server limits the number of pinned (non-finalized) blocks. If the limit is reached, the subscription is stopped with a `stop` event. Unpin blocks by calling `chainHead_v1_unpin` when you are done with them.
- This method is experimental and may not be enabled on public shared RPC endpoints.
- Use `chainHead_v1_unfollow` to cleanly stop the subscription.

## Related Methods

- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop the follow subscription
- [`chainHead_v1_header`](https://www.dwellir.com/docs/bittensor/chainHead_v1_header) -- Get a header for a pinned block
- [`chainHead_v1_body`](https://www.dwellir.com/docs/bittensor/chainHead_v1_body) -- Get the body of a pinned block
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Execute a runtime call at a pinned block
- [`chainHead_v1_storage`](https://www.dwellir.com/docs/bittensor/chainHead_v1_storage) -- Query storage at a pinned block
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Legacy subscription for new block headers

---

## chainHead_v1_header - Bittensor RPC Method

# chainHead_v1_header - Bittensor RPC Method

Returns the SCALE-encoded header of a block that is pinned by an active `chainHead_v1_follow` subscription. Unlike `chainHead_v1_body` and `chainHead_v1_call`, this method returns the result directly (not as a streamed operation), making it the fastest way to inspect block metadata within the follow API.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID from `chainHead_v1_follow`.
- `hash` (`string, required`): Hex-encoded block hash. Must be pinned by the follow subscription.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_header",
  "params": [
    "<followSubscription>",
    "<hash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded SCALE-encoded block header for the requested pinned block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Block number tracking** -- Decode the header to get the block number for display or indexing as each new block arrives.
- **State root verification** -- Verify the state root for light-client proofs or cross-chain bridges.
- **Parent chain traversal** -- Follow `parentHash` links to walk back through recent blocks within the follow window.

## Notes

- This is a synchronous response (not an operation-based flow), so results are returned immediately.
- The block hash must be pinned by the follow subscription. Querying an unpinned or unknown hash returns an error.
- This method is experimental and may not be enabled on public shared RPC endpoints.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start the follow subscription (required first)
- [`chainHead_v1_body`](https://www.dwellir.com/docs/bittensor/chainHead_v1_body) -- Get the block body for a pinned block
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Execute a runtime call at a pinned block
- [`archive_v1_header`](https://www.dwellir.com/docs/bittensor/archive_v1_header) -- Get headers for arbitrary historical blocks
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) -- Legacy method to get a block header

---

## chainHead_v1_stopOperation - Bittensor RPC Method

# chainHead_v1_stopOperation - Bittensor RPC Method

Cancels an in-progress operation (body fetch, runtime call, or storage query) within an active `chainHead_v1_follow` subscription. Operations started by `chainHead_v1_body`, `chainHead_v1_call`, or `chainHead_v1_storage` return an `operationId`; pass that ID to this method to cancel the operation and free server resources.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID from `chainHead_v1_follow`.
- `operationId` (`string, required`): The operation ID returned when the operation was started.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_stopOperation",
  "params": [
    "<followSubscription>",
    "<operationId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`null, required`): Confirms the operation has been stopped.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Timeout handling** -- Cancel an operation that is taking too long to complete, such as a large storage query.
- **Resource management** -- Stop unnecessary operations to stay within the server's concurrent operation limits.
- **Graceful error recovery** -- Cancel a stalled operation and retry with different parameters or at a different block.

## Notes

- This method is part of the new JSON-RPC v2 chain head specification.
- Only operations that have been started (returned `"started"` with an `operationId`) can be stopped.
- `chainHead_v1_header` returns results synchronously and does not use the operation pattern, so it cannot be stopped with this method.
- This method is experimental and may not be enabled on public shared RPC endpoints.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start the follow subscription
- [`chainHead_v1_continue`](https://www.dwellir.com/docs/bittensor/chainHead_v1_continue) -- Resume a paused operation instead of stopping it
- [`chainHead_v1_unfollow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_unfollow) -- Stop the entire follow subscription (also stops all operations)
- [`archive_v1_stopStorage`](https://www.dwellir.com/docs/bittensor/archive_v1_stopStorage) -- Stop an archive storage operation

---

## chainHead_v1_storage - Bittensor RPC Method

# chainHead_v1_storage - Bittensor RPC Method

Queries on-chain storage at a block that is pinned by an active `chainHead_v1_follow` subscription. You can request values, hashes, closest descendant Merkle values, or enumerate descendants for one or more storage keys. Results are streamed via the follow subscription and may be paginated (requiring `chainHead_v1_continue` calls).

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID from `chainHead_v1_follow`.
- `hash` (`string, required`): Hex-encoded block hash. Must be pinned by the follow subscription.
- `items` (`array, required`): Array of storage query items.
- `childTrie` (`string, optional`): Hex-encoded child trie key if querying child storage. Omit for main state trie.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_storage",
  "params": [
    "<followSubscription>",
    "<hash>",
    "<items>",
    "<childTrie>"
  ],
  "id": 1
}
```

## Response Fields

- `result.result` (`"started"` or `"limitReached", required`): `"started"` includes an `operationId`; `"limitReached"` means the node refused to start another operation
- `result.operationId` (`string, required`): Operation ID used to correlate follow-up events when the response is `"started"`
- `result.discardedItems` (`number, required`): Number of requested items the node discarded before starting, when applicable

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "result.result": "<value>",
    "result.operationId": "<value>",
    "result.discardedItems": "0x1"
  }
}
```

## Use Cases

- **Live state reads** -- Query account balances, staking state, or Bittensor subnet parameters at the latest finalized block.
- **Bulk key enumeration** -- Use `descendantsValues` to retrieve all entries under a storage prefix for a given block.
- **Merkle proof generation** -- Use `closestDescendantMerkleValue` for light-client or bridge verification.

## Notes

- Large result sets are paginated with `operationWaitingForContinue` events. Call `chainHead_v1_continue` to get more.
- Use `chainHead_v1_stopOperation` to cancel a slow or unnecessary storage query.
- This method is experimental and may not be enabled on public shared RPC endpoints.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start the follow subscription (required first)
- [`chainHead_v1_continue`](https://www.dwellir.com/docs/bittensor/chainHead_v1_continue) -- Resume paginated storage results
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Runtime call at a followed block
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Legacy storage read
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Legacy paginated key enumeration

---

## chainHead_v1_unfollow - Bittensor RPC Method

# chainHead_v1_unfollow - Bittensor RPC Method

Stops a chain head follow subscription that was started with `chainHead_v1_follow`. This cancels all in-progress operations, unpins all blocks, and frees all server-side resources associated with the subscription. Always call this method when you are done following the chain head to avoid resource leaks.

## Code Examples

## Request Parameters

- `followSubscription` (`string, required`): The subscription ID returned by `chainHead_v1_follow`.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainHead_v1_unfollow",
  "params": [
    "<followSubscription>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`boolean, required`): Indicates whether the follow subscription was successfully stopped. No further events will be delivered.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Graceful shutdown** -- Clean up the follow subscription before disconnecting your WebSocket client.
- **Subscription rotation** -- Stop the current subscription and start a new one (e.g. after a `stop` event indicating too many pinned blocks).
- **Resource management** -- Free server resources when your application no longer needs real-time block notifications.

## Notes

- After calling `unfollow`, the subscription ID becomes invalid and all associated operations are cancelled.
- If the server already sent a `stop` event, the subscription is already terminated, but calling `unfollow` is still safe and recommended for clean client-side state management.
- This method is experimental and may not be enabled on public shared RPC endpoints.

## Related Methods

- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Start a new follow subscription
- [`chainHead_v1_stopOperation`](https://www.dwellir.com/docs/bittensor/chainHead_v1_stopOperation) -- Stop a single operation without ending the subscription
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Legacy unsubscribe from new block headers
- [`chain_unsubscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalizedHeads) -- Legacy unsubscribe from finalized headers

---

## chainSpec_v1_chainName - JSON-RPC Method

# chainSpec_v1_chainName - JSON-RPC Method

## Description

Read static chain specification data such as chain name, ss58 properties, and genesis hash. Use this to configure clients (address format, token info) or verify you are talking to the expected network.

Returns the chain name from the runtime's ChainSpec.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainSpec_v1_chainName",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Human-readable chain name returned by the connected node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Bittensor"
}
```

---

## chainSpec_v1_genesisHash - Bittensor RPC Method

# chainSpec_v1_genesisHash - Bittensor RPC Method

Returns the genesis block hash as defined in the chain specification. The genesis hash is a unique identifier for a Substrate-based chain and is used during transaction signing, network verification, and multi-chain client configuration. On Bittensor mainnet this value is fixed and never changes.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainSpec_v1_genesisHash",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded 32-byte blake2b hash of the genesis block (e.g. `"0x2f07..."`)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Network verification** -- Confirm that the RPC endpoint is serving Bittensor mainnet by comparing the genesis hash against the known value. This prevents accidental transactions on the wrong network.
- **Transaction signing** -- The genesis hash is a required component of the signed extrinsic payload. An incorrect value causes `BadProof` errors.
- **Multi-chain routing** -- In applications that connect to multiple Substrate chains, use the genesis hash to identify which chain a connection belongs to.
- **Client initialization** -- Configure client libraries (like `@polkadot/api`) with the genesis hash to enable proper address encoding and transaction construction.

## Notes

- This method is part of the new JSON-RPC v2 `chainSpec` namespace alongside `chainSpec_v1_chainName` and `chainSpec_v1_properties`.
- The genesis hash is immutable and can be safely cached indefinitely.
- This method is available on most node configurations including public RPC endpoints.

## Related Methods

- [`chainSpec_v1_properties`](https://www.dwellir.com/docs/bittensor/chainSpec_v1_properties) -- Get chain properties (SS58 prefix, token symbol, decimals)
- [`archive_v1_genesisHash`](https://www.dwellir.com/docs/bittensor/archive_v1_genesisHash) -- Get genesis hash via the archive namespace
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) -- Legacy method; pass `0` to get the genesis hash
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Legacy method for chain properties

---

## chainSpec_v1_properties - Bittensor RPC Method

# chainSpec_v1_properties - Bittensor RPC Method

Returns the chain properties as defined in the chain specification. These properties describe the network's address format, native token, and other static configuration. For Bittensor mainnet, this returns the SS58 prefix (42), the native token symbol (TAO), and the token decimals (9).

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chainSpec_v1_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`object, required`): A JSON object with chain-defined properties.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Address formatting** -- Use the `ss58Format` value to correctly encode and decode Bittensor SS58 addresses in your application.
- **Balance display** -- Use `tokenDecimals` and `tokenSymbol` to convert raw balance values (in rao) to human-readable TAO amounts.
- **Client configuration** -- Initialize wallet libraries and UIs with the correct token metadata without hardcoding values.
- **Multi-chain support** -- Dynamically configure your application for different Substrate chains by reading properties at startup.

## Notes

- This method is part of the new JSON-RPC v2 `chainSpec` namespace.
- The returned properties are static and never change for a given chain, so they can be cached.
- This is the v2 equivalent of the legacy `system_properties` method.

## Related Methods

- [`chainSpec_v1_genesisHash`](https://www.dwellir.com/docs/bittensor/chainSpec_v1_genesisHash) -- Get the genesis hash from the chain spec
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Legacy method returning the same information
- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the human-readable chain name

---

## childstate_getKeys - JSON-RPC Method

# childstate_getKeys - JSON-RPC Method

## Description

Read from child tries (nested storage) including listing keys and fetching values. Useful for pallets that isolate state in child trees.

Returns child storage keys for a given child storage root and key prefix.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `prefix` (`string, required`): Hex-encoded key prefix to match inside the child trie. Use `0x` to enumerate the whole trie.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getKeys",
  "params": [
    "<childStorageKey>",
    "0x"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array<string>, required`): Hex-encoded child trie keys matching the prefix at the best block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Notes

- Replace `0xCHILD_STORAGE_KEY` with the actual child trie root for the pallet or contract you are querying.
- Public Bittensor endpoints do not expose a stable example child trie, so you need a chain-specific child storage key before this call will return data.

---

## childstate_getKeysPaged - JSON-RPC Method

# childstate_getKeysPaged - JSON-RPC Method

## Description

Read from child tries (nested storage) including listing keys and fetching values. Useful for pallets that isolate state in child trees.

Returns child storage keys for a given child root and key prefix, paginated.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `prefix` (`string, required`): Hex-encoded prefix to filter child storage keys. Use `0x` for all keys.
- `count` (`number, required`): Maximum number of keys to return in this page.
- `startKey` (`string, optional`): Optional cursor key from the previous page.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getKeysPaged",
  "params": [
    "<childStorageKey>",
    "0x",
    100,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array<string>, required`): Page of matching child trie keys. Returns fewer than `count` items at the end of the scan.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Notes

- Replace `0xCHILD_STORAGE_KEY` with the child trie root you want to inspect.
- Use `"0x"` as the prefix to enumerate the whole trie, then page forward with the returned cursor.

---

## childstate_getKeysPagedAt - Bittensor RPC Method

# childstate_getKeysPagedAt - Bittensor RPC Method

Returns child storage keys matching a prefix with cursor-based pagination at a specific block hash. Child tries are separate state trees used by certain Substrate pallets to isolate their storage from the main state trie. This method is the child-trie equivalent of `state_getKeysPaged` but with explicit block hash targeting.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `prefix` (`string, required`): Hex-encoded prefix to filter child storage keys. Use `"0x"` for all keys.
- `count` (`number, required`): Maximum number of keys to return per page.
- `startKey` (`string, optional`): Hex-encoded key to start after (cursor for pagination). Omit for the first page.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getKeysPagedAt",
  "params": [
    "<childStorageKey>",
    "<prefix>",
    "<count>",
    "<startKey>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of hex-encoded child storage keys matching the prefix. Empty array when no more keys exist.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Child trie enumeration** -- List all keys stored in a child trie, such as contract storage or pallet-specific isolated state.
- **Historical queries** -- Enumerate child storage keys as they existed at a specific block for auditing or analysis.
- **Paginated iteration** -- Iterate over large child tries without loading all keys into memory at once.

## Notes

- Child tries are used by pallets that need isolated storage (e.g. smart contracts). Not all pallets use child storage.
- If the child storage key is invalid or the child trie does not exist, the result will be an empty array.
- Requires an archive node for queries at historical block hashes.

## Related Methods

- [`childstate_getKeysPaged`](https://www.dwellir.com/docs/bittensor/childstate_getKeysPaged) -- Same functionality at the best block
- [`childstate_getStorage`](https://www.dwellir.com/docs/bittensor/childstate_getStorage) -- Read a value from child storage
- [`childstate_getStorageEntries`](https://www.dwellir.com/docs/bittensor/childstate_getStorageEntries) -- Read multiple child storage values at once
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Paginated key enumeration for the main state trie

---

## childstate_getStorage - JSON-RPC Method

# childstate_getStorage - JSON-RPC Method

## Description

Read from child tries (nested storage) including listing keys and fetching values. Useful for pallets that isolate state in child trees.

Returns the SCALE-encoded value for a child storage key.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `key` (`string, required`): Hex-encoded storage key to read from the child trie.
- `blockHash` (`string, optional`): Optional block hash for historical reads. Defaults to the best block.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getStorage",
  "params": [
    "<childStorageKey>",
    "<key>",
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string | null, required`): Hex-encoded SCALE value stored at the child trie key, or `null` when no value exists.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x01020304"
}
```

## Notes

- Replace `0xCHILD_STORAGE_KEY` with the actual child trie root and `0xSTORAGE_KEY` with a concrete key inside that trie.
- Child-storage lookups are runtime-specific. Without a valid child trie root, the node will reject the request or return no data.

---

## childstate_getStorageEntries - Bittensor RPC Method

# childstate_getStorageEntries - Bittensor RPC Method

Returns the values for multiple child storage keys in a single request. This is the batch equivalent of `childstate_getStorage` and is useful when you need to read several entries from a child trie efficiently without making individual RPC calls for each key.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `keys` (`array, required`): Array of hex-encoded storage keys to read from the child trie.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getStorageEntries",
  "params": [
    "<childStorageKey>",
    "<keys>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of hex-encoded SCALE-encoded values, in the same order as the input keys. `null` for keys that do not exist.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Batch child state reads** -- Efficiently read multiple values from a child trie (e.g. contract storage slots) in one round-trip.
- **Snapshot tooling** -- Export multiple child storage entries at a specific block for state snapshots or migrations.
- **Data aggregation** -- Collect multiple related values from a pallet's child storage for dashboards or analytics.

## Notes

- Child tries are used by pallets that isolate their storage (e.g. smart contracts on the EVM layer). Most standard Substrate storage lives in the main trie.
- Values are SCALE-encoded and must be decoded according to the pallet's storage type definitions.
- If the child trie does not exist, all entries will be `null`.

## Related Methods

- [`childstate_getStorage`](https://www.dwellir.com/docs/bittensor/childstate_getStorage) -- Read a single value from child storage
- [`childstate_getKeysPagedAt`](https://www.dwellir.com/docs/bittensor/childstate_getKeysPagedAt) -- Enumerate child storage keys with pagination
- [`childstate_getStorageSize`](https://www.dwellir.com/docs/bittensor/childstate_getStorageSize) -- Get the byte size of a child storage value
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read from the main state trie

---

## childstate_getStorageHash - JSON-RPC Method

# childstate_getStorageHash - JSON-RPC Method

## Description

Read from child tries (nested storage) including listing keys and fetching values. Useful for pallets that isolate state in child trees.

Returns the storage hash for a child storage key.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `key` (`string, required`): Hex-encoded storage key to hash.
- `blockHash` (`string, optional`): Optional block hash for historical reads. Defaults to the best block.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getStorageHash",
  "params": [
    "<childStorageKey>",
    "<key>",
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string | null, required`): Hex-encoded storage hash for the child trie key, or `null` when the key is absent.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x84aea08073c027fa3c5ecf0079b6beec969f8afc5558329b18616949ceb257af"
}
```

## Notes

- Replace `0xCHILD_STORAGE_KEY` with the child trie root and `0xSTORAGE_KEY` with the entry you want to fingerprint.
- Use this when you need a compact integrity check for child storage without fetching the full SCALE payload.

---

## childstate_getStorageSize - Bittensor RPC Method

# childstate_getStorageSize - Bittensor RPC Method

Returns the size in bytes of a value stored in a child trie. This is useful for checking whether a child storage entry exists and determining its size before fetching the full value, which can be helpful for resource planning and bandwidth management.

ze in bytes of the storage value, or `null` if the key does not exist in the child trie. |

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `key` (`string, required`): Hex-encoded storage key within the child trie.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "childstate_getStorageSize",
  "params": [
    "<childStorageKey>",
    "<key>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`number` or `null, required`): Size in bytes of the storage value, or `null` if the key does not exist in the child trie.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Use Cases

- **Existence check** -- Determine whether a child storage key exists without fetching its value (returns `null` if absent).
- **Size estimation** -- Estimate bandwidth and memory requirements before reading large child storage values.
- **Storage monitoring** -- Track the size of contract storage or other child trie entries over time.

## Notes

- Child tries are used by pallets that isolate their storage. Most common Substrate storage lives in the main trie.
- The size returned is for the raw SCALE-encoded value, not the decoded value.
- Returns `null` for non-existent keys rather than 0.

## Related Methods

- [`childstate_getStorage`](https://www.dwellir.com/docs/bittensor/childstate_getStorage) -- Read the actual value for a child storage key
- [`childstate_getStorageEntries`](https://www.dwellir.com/docs/bittensor/childstate_getStorageEntries) -- Batch-read multiple child storage values
- [`childstate_getStorageHash`](https://www.dwellir.com/docs/bittensor/childstate_getStorageHash) -- Get the hash of a child storage value
- [`state_getStorageSize`](https://www.dwellir.com/docs/bittensor/state_getStorageSize) -- Get the size of a main-trie storage value

---

## debug_getBadBlocks - Bittensor RPC Method

# debug_getBadBlocks - Bittensor RPC Method

Returns a list of "bad" blocks that the node has encountered during import or execution. Bad blocks are blocks that failed validation, execution, or consensus checks. This method is part of the EVM-compatible `debug` namespace provided by the Frontier EVM layer on Bittensor and mirrors the Ethereum `debug_getBadBlocks` method.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_getBadBlocks",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of bad block objects, or an empty array if no bad blocks have been recorded.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Node diagnostics** -- Identify blocks that failed import or execution to diagnose consensus issues or runtime bugs.
- **Network health monitoring** -- Detect whether bad blocks are being produced on the Bittensor EVM layer.
- **Forensic analysis** -- Examine the contents of bad blocks to understand why they were rejected.

## Notes

- This method is often disabled on public RPC endpoints. It may require the node to be started with debug flags or `--rpc-methods unsafe`.
- On a healthy network, the result is typically an empty array.
- This is an EVM debug method provided by the Frontier compatibility layer. It operates on the EVM view of blocks, not the Substrate view.

## Related Methods

- [`debug_getRawBlock`](https://www.dwellir.com/docs/bittensor/debug_getRawBlock) -- Get raw block data by number
- [`debug_getRawHeader`](https://www.dwellir.com/docs/bittensor/debug_getRawHeader) -- Get raw header data by number
- [`debug_getRawTransaction`](https://www.dwellir.com/docs/bittensor/debug_getRawTransaction) -- Get raw transaction data by hash
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) -- Get an EVM block by number

---

## debug_getRawBlock - Bittensor RPC Method

# debug_getRawBlock - Bittensor RPC Method

Returns the raw RLP-encoded block data for a given block number on Bittensor's EVM-compatible layer. This is a low-level debug method that provides the raw serialized block as it would appear on the wire in Ethereum's RLP encoding. It is part of the `debug` namespace provided by the Frontier EVM compatibility layer.

## Code Examples

## Request Parameters

- `blockNumber` (`string, required`): Block number as a hex string (e.g. `"0xF4240"` for block 1000000), or `"latest"`, `"earliest"`, `"pending"`.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_getRawBlock",
  "params": [
    "<blockNumber>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded RLP-encoded block data.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Raw block analysis** -- Inspect the exact RLP-encoded representation of a block for debugging encoding/decoding issues.
- **Tooling development** -- Build or test RLP decoders and block parsers against real Bittensor EVM block data.
- **Data archival** -- Store raw block data in its canonical serialized form.

## Notes

- This is an EVM debug method provided by the Frontier compatibility layer. It may be disabled on public RPC endpoints.
- The raw data is RLP-encoded (Ethereum's serialization format), not SCALE-encoded (Substrate's format).
- For Substrate-native block data, use `chain_getBlock` instead.
- May require `--rpc-methods unsafe` or Frontier debug flags enabled.

## Related Methods

- [`debug_getRawHeader`](https://www.dwellir.com/docs/bittensor/debug_getRawHeader) -- Get raw RLP-encoded header data
- [`debug_getRawTransaction`](https://www.dwellir.com/docs/bittensor/debug_getRawTransaction) -- Get raw RLP-encoded transaction data
- [`debug_getBadBlocks`](https://www.dwellir.com/docs/bittensor/debug_getBadBlocks) -- List bad blocks the node has encountered
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) -- Get a decoded EVM block by number
- [`chain_getBlock`](https://www.dwellir.com/docs/bittensor/chain_getBlock) -- Get a Substrate-native block

---

## debug_getRawHeader - Bittensor RPC Method

# debug_getRawHeader - Bittensor RPC Method

Returns the raw RLP-encoded block header for a given block number on Bittensor's EVM-compatible layer. This is a lightweight debug method that provides just the header portion of the block in Ethereum's RLP encoding, without the transaction body. It is part of the `debug` namespace provided by the Frontier EVM compatibility layer.

## Code Examples

## Request Parameters

- `blockNumber` (`string, required`): Block number as a hex string (e.g. `"0xF4240"`), or `"latest"`, `"earliest"`, `"pending"`.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_getRawHeader",
  "params": [
    "<blockNumber>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded RLP-encoded block header data.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Header verification** -- Inspect the raw RLP-encoded header to verify encoding correctness or debug header-related issues.
- **Light client development** -- Obtain raw headers for building or testing EVM-compatible light client implementations.
- **Tooling development** -- Test RLP header decoders against real Bittensor EVM header data.

## Notes

- This is an EVM debug method provided by the Frontier compatibility layer. It may be disabled on public RPC endpoints.
- The raw data uses RLP encoding (Ethereum format), not SCALE encoding (Substrate format).
- For the decoded header, use `eth_getBlockByNumber` with the second parameter set to `false`.
- May require `--rpc-methods unsafe` or Frontier debug flags enabled.

## Related Methods

- [`debug_getRawBlock`](https://www.dwellir.com/docs/bittensor/debug_getRawBlock) -- Get raw RLP-encoded full block data
- [`debug_getRawTransaction`](https://www.dwellir.com/docs/bittensor/debug_getRawTransaction) -- Get raw RLP-encoded transaction data
- [`debug_getBadBlocks`](https://www.dwellir.com/docs/bittensor/debug_getBadBlocks) -- List bad blocks
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) -- Get a decoded EVM block
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) -- Get a Substrate-native block header

---

## debug_getRawReceipts - JSON-RPC Method

# debug_getRawReceipts - JSON-RPC Method

## Description

Fetch raw SCALE‑encoded headers, blocks, or receipts for deep debugging or tooling. Usually disabled on public RPC.

Returns raw receipts for a block. Applies to Frontier EVM context.

## Code Examples

## Request Parameters

- `blockHash` (`string, required`): Hex-encoded block hash whose Frontier receipts you want to inspect.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_getRawReceipts",
  "params": [
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array<string> | null, required`): Array of raw RLP-encoded receipt bytes for the block, or `null` when receipts are unavailable.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

---

## debug_getRawTransaction - Bittensor RPC Method

# debug_getRawTransaction - Bittensor RPC Method

Returns the raw RLP-encoded transaction data for a given transaction hash on Bittensor's EVM-compatible layer. This provides the exact bytes of the signed transaction as it was submitted to the network, before any decoding. It is part of the `debug` namespace provided by the Frontier EVM compatibility layer.

## Code Examples

## Request Parameters

- `txHash` (`string, required`): The EVM transaction hash (32-byte hex string with `0x` prefix).

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_getRawTransaction",
  "params": [
    "<txHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string` or `null, required`): Hex-encoded RLP-encoded transaction data, or `null` if the transaction is not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Transaction debugging** -- Inspect the raw signed transaction bytes to debug encoding, signing, or submission issues on Bittensor's EVM layer.
- **Transaction replay** -- Obtain the raw transaction to replay it on a test network or local development environment.
- **Signature verification** -- Extract the raw transaction for independent signature verification.
- **Tooling development** -- Test RLP transaction decoders against real Bittensor EVM transaction data.

## Notes

- This is an EVM debug method provided by the Frontier compatibility layer. It may be disabled on public RPC endpoints.
- The raw data uses RLP encoding (Ethereum format). The transaction includes nonce, gas price, gas limit, to, value, data, and signature fields.
- Returns `null` if the transaction hash is not found or has been pruned.
- May require `--rpc-methods unsafe` or Frontier debug flags enabled.

## Related Methods

- [`debug_getRawBlock`](https://www.dwellir.com/docs/bittensor/debug_getRawBlock) -- Get raw RLP-encoded block data
- [`debug_getRawHeader`](https://www.dwellir.com/docs/bittensor/debug_getRawHeader) -- Get raw RLP-encoded header data
- [`debug_getBadBlocks`](https://www.dwellir.com/docs/bittensor/debug_getBadBlocks) -- List bad blocks
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/bittensor/eth_getTransactionByHash) -- Get a decoded EVM transaction
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/bittensor/eth_getTransactionReceipt) -- Get the transaction receipt

---

## delegateInfo_getDelegate - Bittensor RPC Method

# delegateInfo_getDelegate - Bittensor RPC Method

## Overview

The `delegateInfo_getDelegate` method returns SCALE-encoded information for a specific delegate identified by their hotkey bytes. This is the targeted version of `delegateInfo_getDelegates` -- use it when you already know which delegate you want to inspect.

Delegates are validator hotkeys that accept nominated TAO stake from other accounts. Each delegate has a configurable take rate, a list of nominators, and registrations across one or more subnets. This method is commonly used in staking UIs to show detailed delegate profiles before a user commits to delegation.

zed `u16` value |
\| `nominators` | `Vec<(AccountId32, Vec<(NetUid, stake)>)>` | Nominator entries grouped by subnet and stake amount |
\| `owner_ss58` | `AccountId32` | The coldkey that owns this delegate hotkey |
\| `registrations` | `Vec<Compact<u16>>` | Subnet netuids where this delegate is registered |
\| `validator_permits` | `Vec<Compact<u16>>` | Subnet netuids where this delegate has validator permits |
\| `return_per_1000` | `Compact<u64>` | Estimated delegator return per 1000 TAO staked |
\| `total_daily_return` | `Compact<u64>` | Estimated total daily delegator return |

Returns `null` if the provided account is not a registered delegate.

## SCALE Decoding

The raw response requires SCALE decoding with Bittensor type definitions.

**Using `@polkadot/api`:** Register Bittensor custom types (including the `DelegateInfo` struct) before creating the API instance. The response decodes to a single `DelegateInfo` struct (or `Option<DelegateInfo>` that can be empty).

**Using `scalecodec` (Python):** The `bittensor` SDK handles decoding internally via `sub.get_delegate_by_hotkey()`. For manual decoding, parse the byte-array result as an `Option<DelegateInfo>` using the Bittensor type registry.

**Key decoding notes:**

- The `take` field is normalized over the full `u16` range rather than simple basis points
- All RAO amounts should be divided by `1e9` for TAO display values
- `nominators` is a nested structure that records delegations per subnet, which is why the payload can grow large for active delegates
- An empty/null result means the hotkey is not a registered delegate

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `delegate_account_id` (`number[], required`): Raw 32-byte hotkey public key as a JSON byte array
- `at` (`BlockHash, optional`): Optional block hash to query at a specific block. Pass `null` for the latest state

## Request Example

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const delegateHotkey = '0x0000000000000000000000000000000000000000000000000000000000000000';
const result = await api.rpc.delegateInfo.getDelegate(delegateHotkey);

if (result.isEmpty) {
  console.log('Delegate not found');
} else {
  console.log('Raw SCALE result:', result.toHex().slice(0, 80), '...');
}

await api.disconnect();
```

## Response Fields

- `delegate_ss58` (`AccountId32, required`): The delegate hotkey
- `take` (`Compact<u16>, required`): Delegate take stored as a normalized `u16` value
- `nominators` (`Vec<(AccountId32, Vec<(NetUid, stake)>)>, required`): Nominator entries grouped by subnet and stake amount
- `owner_ss58` (`AccountId32, required`): The coldkey that owns this delegate hotkey
- `registrations` (`Vec<Compact<u16>>, required`): Subnet netuids where this delegate is registered
- `validator_permits` (`Vec<Compact<u16>>, required`): Subnet netuids where this delegate has validator permits
- `return_per_1000` (`Compact<u64>, required`): Estimated delegator return per 1000 TAO staked
- `total_daily_return` (`Compact<u64>, required`): Estimated total daily delegator return

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "delegate_ss58": "<value>",
    "take": "<value>",
    "nominators": "<value>",
    "owner_ss58": "<value>",
    "registrations": "<value>",
    "validator_permits": "<value>",
    "return_per_1000": "<value>",
    "total_daily_return": "<value>"
  }
}
```

## Error Responses

### Error 1

### Error 2

### Error 3

### Error 4

### Error 5

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Replace with a real delegate hotkey as 32 raw bytes
delegate_pubkey = [0] * 32

payload = {
    'jsonrpc': '2.0',
    'method': 'delegateInfo_getDelegate',
    'params': [delegate_pubkey, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    print(f"SCALE byte count: {len(result['result'])}")
else:
    print('Delegate not found or not registered')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# delegate = sub.get_delegate_by_hotkey(delegate_ss58)
# print(f"Take: {delegate.take}, Nominators: {len(delegate.nominators)}")
```

### Full Python example with delegate analysis

```python
import bittensor as bt

sub = bt.subtensor(network='finney')

# Look up a specific delegate by SS58 hotkey address
delegate_ss58 = '5F4tQyWrhfGVcNhoqeiNsR6KjBCapnXJYpGyexNFpbqR2Yq7'
delegate = sub.get_delegate_by_hotkey(delegate_ss58)

if delegate is None:
    print(f"Hotkey {delegate_ss58} is not a registered delegate")
else:
    # Delegate profile
    print(f"Delegate: {delegate.hotkey_ss58}")
    print(f"Owner:    {delegate.owner_ss58}")
    print(f"Take:     {delegate.take * 100:.1f}%")
    print(f"Nominators: {len(delegate.nominators)}")
    print(f"Total stake: {delegate.total_stake.tao:,.2f} TAO")

    # Subnet presence
    print(f"Registered on subnets: {delegate.registrations}")
    print(f"Validator permits on:  {delegate.validator_permits}")

    # Yield estimation
    return_per_1000 = delegate.return_per_1000.tao
    print(f"Return per 1000 TAO: {return_per_1000:.4f} TAO")

    # Top nominators
    sorted_noms = sorted(
        delegate.nominators,
        key=lambda item: sum(amount.tao for _, amount in item[1]),
        reverse=True
    )
    print("\nTop 5 nominators:")
    for addr, stakes in sorted_noms[:5]:
        total = sum(amount.tao for _, amount in stakes)
        print(f"  {addr}: {total:,.2f} TAO across {len(stakes)} subnet entries")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const delegateHotkey = '0x0000000000000000000000000000000000000000000000000000000000000000';
const result = await api.rpc.delegateInfo.getDelegate(delegateHotkey);

if (result.isEmpty) {
  console.log('Delegate not found');
} else {
  console.log('Raw SCALE result:', result.toHex().slice(0, 80), '...');
}

await api.disconnect();
```

### Historical query at a specific block

```python
import requests

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
delegate_pubkey = [0] * 32  # delegate hotkey as raw bytes

# Query at a specific block hash for historical delegate state
block_hash = '0xabc123...'  # a past block hash
payload = {
    'jsonrpc': '2.0',
    'method': 'delegateInfo_getDelegate',
    'params': [delegate_pubkey, block_hash],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()
# Compare with current state to track changes over time
```

## Common Use Cases

- **Validator research** — Inspect a specific delegate's take rate, nominator count, and subnet registrations before delegating TAO.
- **Delegation decisions** — Compare the return per 1000 TAO across candidates to pick the best delegate.
- **Portfolio tracking** — Monitor a delegate's performance over time by querying at specific block hashes.
- **Staking UIs** — Display a delegate's profile page with all relevant staking metrics.
- **Alerting** — Watch for changes in a delegate's take rate or registration status by polling periodically.
- **Nominator analysis** — Inspect the full list of nominators to understand stake concentration and whale exposure.

## Related Methods

- [`delegateInfo_getDelegates`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegates) — Get info for all registered delegates
- [`delegateInfo_getDelegated`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegated) — Get delegations made by a specific account
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph for a subnet
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed neuron info by UID
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic subnet info including emission data

---

## delegateInfo_getDelegated - Bittensor RPC Method

# delegateInfo_getDelegated - Bittensor RPC Method

## Overview

The `delegateInfo_getDelegated` method returns SCALE-encoded information about all delegations made by a specific account (coldkey). While `delegateInfo_getDelegates` lists delegate profiles, this method answers the question: "Where has this account delegated its TAO?"

This is the primary method for building portfolio views and staking dashboards that show a user's active delegations, the delegates they have nominated, and how much TAO they have staked with each.

zed `u16` value |
\| `nominators` | `Vec<(AccountId32, Vec<(NetUid, stake)>)>` | Full nominator list for the delegate, grouped by subnet |
\| `owner_ss58` | `AccountId32` | The coldkey that owns the delegate hotkey |
\| `registrations` | `Vec<Compact<u16>>` | Subnet netuids where the delegate is registered |
\| `validator_permits` | `Vec<Compact<u16>>` | Subnet netuids where the delegate has validator permits |
\| `return_per_1000` | `Compact<u64>` | Estimated delegator return per 1000 TAO staked |
\| `total_daily_return` | `Compact<u64>` | Estimated total daily delegator return |
\| `netuid` / `alpha_stake` | `(u16, Compact<u64>)` | The subnet identifier and this delegator's stake on that subnet |

Returns an empty list if the account has no active delegations.

## SCALE Decoding

The raw response requires SCALE decoding with Bittensor-specific type definitions.

**Using `@polkadot/api`:** Register the `DelegateInfo` type definition before creating the API instance. The result decodes to `Vec<(DelegateInfo, (u16, Compact<u64>))>`.

**Using `scalecodec` (Python):** The `bittensor` SDK provides `sub.get_delegated()` helpers. For manual decoding, parse the response as a vector pairing `DelegateInfo` with subnet-specific stake data.

**Key decoding notes:**

- Each entry pairs a full `DelegateInfo` struct with the queried account's specific stake amount
- The `nominators` list within each `DelegateInfo` includes all nominators, not just the queried account -- use this to see your share relative to other nominators
- To find your specific stake amount for each delegation, look up the queried `AccountId32` within the `nominators` vector, or use the second tuple element
- Amounts are in RAO (divide by `1e9` for TAO)

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `delegator_account_id` (`number[], required`): Raw 32-byte coldkey public key as a JSON byte array
- `at` (`BlockHash, optional`): Optional block hash to query at a specific block. Pass `null` for the latest state

## Request Example

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const coldkey = '0x0000000000000000000000000000000000000000000000000000000000000000';
const result = await api.rpc.delegateInfo.getDelegated(coldkey);

if (result.isEmpty) {
  console.log('No delegations found');
} else {
  console.log('Raw SCALE result:', result.toHex().slice(0, 80), '...');
}

await api.disconnect();
```

## Response Fields

- `delegate_ss58` (`AccountId32, required`): The delegate hotkey this account has staked with
- `take` (`Compact<u16>, required`): The delegate take stored as a normalized `u16` value
- `nominators` (`Vec<(AccountId32, Vec<(NetUid, stake)>)>, required`): Full nominator list for the delegate, grouped by subnet
- `owner_ss58` (`AccountId32, required`): The coldkey that owns the delegate hotkey
- `registrations` (`Vec<Compact<u16>>, required`): Subnet netuids where the delegate is registered
- `validator_permits` (`Vec<Compact<u16>>, required`): Subnet netuids where the delegate has validator permits
- `return_per_1000` (`Compact<u64>, required`): Estimated delegator return per 1000 TAO staked
- `total_daily_return` (`Compact<u64>, required`): Estimated total daily delegator return
- `netuid` / `alpha_stake` (`(u16, Compact<u64>), required`): The subnet identifier and this delegator's stake on that subnet

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "delegate_ss58": "<value>",
    "take": "<value>",
    "nominators": "<value>",
    "owner_ss58": "<value>",
    "registrations": "<value>",
    "validator_permits": "<value>",
    "return_per_1000": "<value>",
    "total_daily_return": "<value>",
    "netuid` / `alpha_stake": "<value>"
  }
}
```

## Error Responses

### Error 1

### Error 2

### Error 3

### Error 4

### Error 5

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Replace with the coldkey bytes you want to inspect
coldkey_pubkey = [0] * 32

payload = {
    'jsonrpc': '2.0',
    'method': 'delegateInfo_getDelegated',
    'params': [coldkey_pubkey, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    print(f"SCALE byte count: {len(result['result'])}")
else:
    print('No delegations found for this account')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# delegated = sub.get_delegated(coldkey_ss58)
# for delegation in delegated:
#     print(f"Delegate: {delegation[0].hotkey_ss58}, Amount: {delegation[1]}")
```

### Full Python portfolio analysis

```python
import bittensor as bt

sub = bt.subtensor(network='finney')

coldkey_ss58 = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
delegations = sub.get_delegated(coldkey_ss58)

if not delegations:
    print(f"No delegations found for {coldkey_ss58}")
else:
    total_staked = 0
    total_daily_yield = 0

    print(f"Delegations for {coldkey_ss58[:18]}...\n")
    print(f"{'Delegate':<20} {'Staked (TAO)':>15} {'Take %':>8} {'Subnets':>10} {'Est. Daily':>12}")
    print("-" * 70)

    for delegate_info, staked_amount in delegations:
        staked_tao = staked_amount.tao
        take_pct = delegate_info.take * 100
        subnets = len(delegate_info.registrations)
        daily_return = delegate_info.total_daily_return.tao

        # Estimate your share of daily return
        if delegate_info.total_stake.tao > 0:
            your_share = (staked_tao / delegate_info.total_stake.tao) * daily_return * (1 - delegate_info.take)
        else:
            your_share = 0

        total_staked += staked_tao
        total_daily_yield += your_share

        print(f"{delegate_info.hotkey_ss58[:18]}.. {staked_tao:>14,.2f} {take_pct:>7.1f}% {subnets:>10} {your_share:>11,.4f}")

    print("-" * 70)
    print(f"{'Total':>20} {total_staked:>14,.2f} {'':>8} {'':>10} {total_daily_yield:>11,.4f}")
    print(f"\nEstimated annual yield: {total_daily_yield * 365:,.2f} TAO ({(total_daily_yield * 365 / total_staked * 100):.2f}%)")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const coldkey = '0x0000000000000000000000000000000000000000000000000000000000000000';
const result = await api.rpc.delegateInfo.getDelegated(coldkey);

if (result.isEmpty) {
  console.log('No delegations found');
} else {
  console.log('Raw SCALE result:', result.toHex().slice(0, 80), '...');
}

await api.disconnect();
```

### Historical delegation tracking

```python
import requests

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
coldkey_pubkey = '0x...'

# Get delegation state at two different blocks to track changes
block_hashes = [
    '0xabc...',  # earlier block
    '0xdef...',  # later block
]

for block_hash in block_hashes:
    payload = {
        'jsonrpc': '2.0',
        'method': 'delegateInfo_getDelegated',
        'params': [coldkey_pubkey, block_hash],
        'id': 1
    }
    response = requests.post(url, json=payload)
    result = response.json()
    print(f"Block {block_hash[:10]}...: {len(result.get('result', '')) // 2} bytes")
```

## Common Use Cases

- **Portfolio tracking** — Show a user all their active TAO delegations and the amount staked with each delegate.
- **Staking analytics** — Calculate total staked TAO, weighted average take rate, and estimated daily yield for a wallet.
- **Nomination monitoring** — Track which delegates an account has nominated and alert on changes (e.g., delegate deregistration).
- **Tax reporting** — Build delegation history by querying at sequential block hashes for staking income calculations.
- **Whale tracking** — Monitor large accounts' delegation patterns to understand network stake distribution.
- **Yield optimization** — Compare your current delegation spread against optimal allocation based on delegate performance metrics.

## Related Methods

- [`delegateInfo_getDelegates`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegates) — Get info for all registered delegates
- [`delegateInfo_getDelegate`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegate) — Get info for a single delegate by hotkey
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph for a subnet
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get configuration info for all subnets
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic subnet info including emission rates

---

## delegateInfo_getDelegates - Bittensor RPC Method

# delegateInfo_getDelegates - Bittensor RPC Method

## Overview

The `delegateInfo_getDelegates` method returns a SCALE-encoded byte array containing information about every registered delegate on the Bittensor network. Delegates are hotkeys that have registered to accept TAO stake from other accounts (nominators). This method is the primary way to discover all available delegates for staking decisions.

In Bittensor, delegation is a core economic mechanism. TAO holders who do not run their own validators can delegate their stake to a delegate hotkey and earn a share of the delegate's emissions. Each delegate sets a take rate (percentage of emissions they keep), making this data critical for yield optimization.

Delegates are distinct from regular validators in that they have explicitly opted in to accept external nominations. Not every validator is a delegate -- only those who have called the `become_delegate` extrinsic. The default take rate is 18% (1800 basis points), but delegates can adjust this between 0% and 100%. When evaluating delegates, consider both the take rate and the delegate's total emissions across subnets.

The data returned by this method includes each delegate's subnet registrations, allowing you to see which subnets a delegate is active on, which subnets they hold validator permits for, and how their stake and emissions are distributed.

## SCALE Decoding

The raw response is a JSON array of SCALE bytes that must be decoded before use. There are two main approaches:

**Using `@polkadot/api` (JavaScript/TypeScript):** Register Bittensor custom types before creating the API instance. The Bittensor node exposes its type definitions through the RPC metadata. In practice, define a local type bundle or custom registry entry for `DelegateInfo`, then decode the payload as `Vec<DelegateInfo>` with `createType` or `registry.createType`.

**Using `scalecodec` (Python):** The `bittensor` Python SDK wraps SCALE decoding internally. If you need manual decoding, use the `scalecodec` library with a Bittensor-specific type registry that defines `DelegateInfo` as a struct containing the fields listed above.

**Key decoding notes:**

- The `take` field uses basis points: divide by 10000 to get a percentage (e.g., `1800` = 18%)
- Stake amounts in `nominators` are in RAO: divide by `1e9` to convert to TAO
- The `return_per_1000` field is also in RAO and represents estimated yield per 1000 TAO staked over a period
- `registrations` and `validator_permits` contain subnet netuid values (u16)
- The `nominators` list can be very large for popular delegates (thousands of entries), which is the main driver of response size
- `validator_permits` is a subset of `registrations` -- a delegate can be registered on a subnet without having a validator permit if they do not have enough stake to qualify
- The `owner_ss58` field is the coldkey that controls the delegate hotkey. This is important for identifying the operator behind a delegate
- `total_daily_return` is an estimate based on recent emission history and may fluctuate between tempos

## Code Examples

### Using SubstrateExamples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "delegateInfo_getDelegates",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `delegate_ss58` (`AccountId32, required`): The delegate's hotkey SS58 address
- `take` (`Compact<u16>, required`): Delegate's take rate (percentage of emissions retained, in basis points). Default is `1800` (18%). Range: 0--10000
- `nominators` (`Vec<(AccountId32, Compact<u64>)>, required`): List of nominator accounts and their staked amounts (in RAO, where 1 TAO = 10^9 RAO)
- `owner_ss58` (`AccountId32, required`): The coldkey that owns this delegate hotkey
- `registrations` (`Vec<Compact<u16>>, required`): Subnet netuids where this delegate is registered
- `validator_permits` (`Vec<Compact<u16>>, required`): Subnet netuids where this delegate has validator permits
- `return_per_1000` (`Compact<u64>, required`): Estimated return per 1000 TAO staked (in RAO)
- `total_daily_return` (`Compact<u64>, required`): Total daily emissions earned by this delegate (in RAO)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "delegate_ss58": "<value>",
    "take": "<value>",
    "nominators": "<value>",
    "owner_ss58": "<value>",
    "registrations": "<value>",
    "validator_permits": "<value>",
    "return_per_1000": "<value>",
    "total_daily_return": "<value>"
  }
}
```

## Error Responses

### Error 1

### Error 2

### Error 3

### Error 4

### Error 5

### Error 6

### Decode with Python (bittensor SDK)

```python
import requests
import json

# Query all delegates via JSON-RPC
url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
payload = {
    'jsonrpc': '2.0',
    'method': 'delegateInfo_getDelegates',
    'params': [],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

# The result is a JSON array of SCALE bytes
scale_bytes = result['result']
print(f'Response size: {len(scale_bytes)} bytes')

# To decode, use the bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# delegates = sub.get_delegates()
# for d in delegates[:5]:
#     print(f"Delegate: {d.hotkey_ss58}, Take: {d.take}, Nominators: {len(d.nominators)}")
```

### Full Python example with analysis

```python
import bittensor as bt

sub = bt.subtensor(network='finney')
delegates = sub.get_delegates()

# Sort delegates by total stake (descending)
delegates_sorted = sorted(delegates, key=lambda d: d.total_stake, reverse=True)

# Display top 10 delegates
print(f"{'Hotkey':<20} {'Take %':>8} {'Nominators':>12} {'Total Stake (TAO)':>20}")
print("-" * 64)
for d in delegates_sorted[:10]:
    take_pct = d.take * 100
    total_tao = d.total_stake.tao
    print(f"{d.hotkey_ss58[:18]}.. {take_pct:>7.1f}% {len(d.nominators):>12} {total_tao:>20,.2f}")

# Calculate network-wide delegation statistics
total_delegated = sum(d.total_stake.tao for d in delegates)
avg_take = sum(d.take for d in delegates) / len(delegates) * 100
print(f"\nTotal delegates: {len(delegates)}")
print(f"Total TAO delegated: {total_delegated:,.2f}")
print(f"Average take rate: {avg_take:.1f}%")
```

### Decode with JavaScript (@polkadot/api)

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Call the custom RPC method
const result = await api.rpc.delegateInfo.getDelegates();

// The result is SCALE-encoded; decode with Bittensor type registry
// Register Bittensor types before creating the API instance for automatic decoding
console.log('Raw SCALE result length:', result.length);

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "delegateInfo_getDelegates",
    "params": [],
    "id": 1
  }'
```

ze considerations:\*\* On mainnet with hundreds of active delegates, each with potentially thousands of nominators, the response can exceed 5 MB. Plan your HTTP client buffer sizes and parsing strategy accordingly. For bandwidth-constrained environments, consider querying individual delegates via `delegateInfo_getDelegate` instead.

## Common Use Cases

- **Staking dashboards** — Display all available delegates with their take rates, total stake, and nominator counts so users can choose where to delegate TAO.
- **Delegate discovery** — Help new TAO holders find active, well-performing delegates across different subnets.
- **Yield analysis** — Compare delegate returns, take rates, and historical performance to optimize staking yield. Use `return_per_1000` to estimate annualized yields.
- **Network analytics** — Track total delegation across the network, concentration of stake, and delegate activity. Identify the Nakamoto coefficient for delegation.
- **Validator monitoring** — Monitor which subnets delegates are registered on and whether they hold validator permits. Alert when delegates lose permits.
- **Subnet coverage analysis** — Identify which subnets have the most delegate coverage and which are underserved by comparing `registrations` across all delegates.
- **Take rate alerts** — Monitor delegate take rates for changes. A delegate lowering their take may signal increased competition for nominations; a raise may indicate reduced yield for nominators.
- **Nominator concentration** — Analyze the distribution of stake across nominators for each delegate. High concentration (few large nominators) versus wide distribution affects risk for smaller stakers.
- **Historical snapshots** — While this method does not accept a block hash parameter directly, you can use it alongside `chain_getBlockHash` and block-specific queries to reconstruct delegate state over time.

## Related Methods

- [`delegateInfo_getDelegate`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegate) — Get info for a single delegate by hotkey (lighter than fetching all)
- [`delegateInfo_getDelegated`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegated) — Get delegations made by a specific coldkey account
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph for a subnet (includes validator/miner data)
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get configuration info for all subnets (complements delegate data)
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic subnet economics (emissions that delegates earn)
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed neuron info by UID within a subnet
- [`swap_currentAlphaPrice`](https://www.dwellir.com/docs/bittensor/swap_currentAlphaPrice) — Get the current alpha token price for a subnet

---

## eth_accounts - Bittensor RPC Method

Returns a list of addresses owned by the client on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for AI/ML developers, subnet operators, and teams building decentralized machine learning applications in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/bittensor/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/bittensor/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Bittensor RPC Method

Returns the number of the most recent block on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_blockNumber` is fundamental for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Syncing Applications** - Keep your dApp in sync with the latest Bittensor blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Bittensor block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Bittensor block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Bittensor block: {block_number}')

# eth_blockNumber - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Bittensor block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Bittensor block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Bittensor:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Bittensor:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Bittensor node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/bittensor/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check if node is still syncing

---

## eth_call - Bittensor RPC Method

Executes a new message call immediately without creating a transaction on Bittensor. Used for reading smart contract state.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_call` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Bittensor
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';
const walletAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Bittensor. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Bittensor
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Bittensor

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "data": "0x70a08231000000000000000000000000156e431cc96e0e3b70c97214d869c9bc4b5bbd21"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "data": "0x70a08231000000000000000000000000156e431cc96e0e3b70c97214d869c9bc4b5bbd21"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
    '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    data := common.FromHex("0x70a08231000000000000000000000000156e431cc96e0e3b70c97214d869c9bc4b5bbd21")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Bittensor RPC Method

Returns the chain ID used for transaction signing on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_chainId` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Bittensor RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/bittensor/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check sync status

---

## eth_coinbase - Bittensor RPC Method

Checks the legacy `eth_coinbase` compatibility method on Bittensor. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for AI/ML developers, subnet operators, and teams building decentralized machine learning applications when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Bittensor coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Bittensor coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Bittensor coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Bittensor coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Bittensor coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Bittensor nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/bittensor/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/bittensor/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/bittensor/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Bittensor RPC Method

Estimates the gas necessary to execute a transaction on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Bittensor
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Bittensor.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Bittensor may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "to": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "to": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Bittensor RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/bittensor/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Bittensor RPC Method

Returns historical gas fee data on Bittensor, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Bittensor without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Bittensor.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Bittensor) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/bittensor/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/bittensor/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Bittensor RPC Method

Returns the current gas price on Bittensor in wei.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Bittensor
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Bittensor. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Bittensor, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Bittensor: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Bittensor supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Bittensor RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/bittensor/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/bittensor/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Bittensor RPC Method

Returns the balance of a given address on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getBalance` is fundamental for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Bittensor
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Bittensor
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Bittensor. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Bittensor`);
  return balance;
}

displayBalance('0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Bittensor at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Bittensor')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Bittensor')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Bittensor.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Bittensor RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/bittensor/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/bittensor/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Bittensor RPC Method

Returns information about a block by hash on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getBlockByHash` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Bittensor
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Bittensor.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Bittensor RPC Method
verify_block_still_canonical('0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Bittensor.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Bittensor RPC Method

Returns information about a block by block number on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getBlockByNumber` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Bittensor
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Bittensor with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Bittensor. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Bittensor RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/bittensor/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/bittensor/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Bittensor RPC Method

# eth_getBlockReceipts - Bittensor RPC Method

Returns all transaction receipts for a block on Bittensor. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getBlockReceipts` is useful for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Bittensor, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Bittensor. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0xbdee85c3ba64aa4a13d1bc6766b22e4f1baba2268fc1f9fbab5e4d52d85edb41",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/bittensor/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/bittensor/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Bittensor RPC Method

Returns the bytecode at a given address on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getCode` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Bittensor
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Bittensor:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Bittensor:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Bittensor RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/bittensor/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/bittensor/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Bittensor RPC Method

Polls a filter on Bittensor and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getFilterChanges` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Bittensor
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Bittensor RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Bittensor:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/bittensor/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/bittensor/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/bittensor/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/bittensor/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/bittensor/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Bittensor RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Bittensor. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getFilterLogs` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Bittensor
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Bittensor RPC Method
FILTER_ID=$(curl -s -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/bittensor/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/bittensor/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/bittensor/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Bittensor RPC Method

# eth_getLogs - Bittensor RPC Method

Returns an array of all logs matching a given filter object on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Bittensor for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Bittensor
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Bittensor.

```javascript
const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Bittensor
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Bittensor

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/bittensor/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Bittensor RPC Method

Returns the value from a storage position at a given address on Bittensor. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getStorageAt` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Bittensor:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Bittensor:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/bittensor/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/bittensor/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/bittensor/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Bittensor RPC Method

# eth_getTransactionByHash - Bittensor RPC Method

Returns the information about a transaction by transaction hash on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getTransactionByHash` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Bittensor
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Bittensor. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Bittensor. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/bittensor/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Bittensor RPC Method

Returns the number of transactions sent from an address on Bittensor, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getTransactionCount` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Bittensor
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Bittensor nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21'
nonce = get_transaction_count(address)
print(f'Bittensor nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Bittensor nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Bittensor:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/bittensor/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/bittensor/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/bittensor/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Bittensor RPC Method

# eth_getTransactionReceipt - Bittensor RPC Method

Returns the receipt of a transaction by transaction hash on Bittensor. Receipt is only available for mined transactions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_getTransactionReceipt` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Bittensor and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Bittensor. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Bittensor, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0xb39db715c7f2ea113073175f071afe67a2c809bd30694c1a768d970ed159a1d7")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/bittensor/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/bittensor/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Bittensor RPC Method

Returns the legacy `eth_hashrate` compatibility value on Bittensor. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Bittensor hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Bittensor hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Bittensor hashrate: {hashrate} H/s')

# eth_hashrate - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Bittensor hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Bittensor hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/bittensor/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/bittensor/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Bittensor RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Bittensor. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Bittensor
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Bittensor priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Bittensor priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Bittensor priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Bittensor:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Bittensor:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/bittensor/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/bittensor/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/bittensor/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Bittensor RPC Method

Checks the legacy `eth_mining` compatibility method on Bittensor. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Bittensor mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Bittensor mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Bittensor mining: {mining["result"]}')

# eth_mining - Bittensor RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Bittensor mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Bittensor mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/bittensor/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/bittensor/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Bittensor RPC Method

Creates a filter on Bittensor that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_newBlockFilter` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Block Monitoring** - Detect new blocks on Bittensor as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Bittensor RPC Method
FILTER_ID=$(curl -s -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Bittensor:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/bittensor/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/bittensor/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Bittensor RPC Method

Creates a filter object on Bittensor based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_newFilter` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Event Monitoring** - Subscribe to specific contract events on Bittensor such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Bittensor:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/bittensor/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/bittensor/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/bittensor/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/bittensor/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Bittensor RPC Method

Creates a filter on Bittensor that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Mempool Monitoring** - Observe unconfirmed transactions on Bittensor to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Bittensor RPC Method
FILTER_ID=$(curl -s -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Bittensor...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Bittensor:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/bittensor/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/bittensor/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Bittensor RPC Method

# eth_protocolVersion - Bittensor RPC Method

Returns the current Ethereum protocol version used by the Bittensor node.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_protocolVersion` is useful for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Current Dwellir Result

Dwellir's shared Bittensor EVM endpoint currently returns the integer-compatible value `1` from `eth_protocolVersion`. Treat that value as a simple compatibility signal for this surface instead of assuming the response is always a hex string.

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`INTEGER, required`): Integer-compatible protocol version value. Dwellir's shared Bittensor endpoint currently returns 1.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 1
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1,
  }),
});

const { result } = await response.json();
const version = typeof result === 'string' ? Number(result) : result;

console.log('Bittensor protocol version:', version);
```

```python
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_protocolVersion',
        'params': [],
        'id': 1,
    },
)

result = response.json()['result']
version = int(result)
print(f'Bittensor protocol version: {version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result int
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Bittensor protocol version: %d\n", result)
}
```

## Common Use Cases

### 1. Gate compatibility checks on the raw value

```javascript
async function assertBittensorProtocol(provider) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = typeof result === 'string' ? Number(result) : result;

  if (version !== 1) {
    throw new Error(`Unexpected Bittensor protocol version: ${version}`);
  }

  return version;
}
```

### 2. Record client/version pairs across a fleet

```javascript
async function auditBittensorNodes(provider) {
  const [protocolVersion, clientVersion] = await Promise.all([
    provider.send('eth_protocolVersion', []),
    provider.send('web3_clientVersion', []),
  ]);

  return {
    protocolVersion: typeof protocolVersion === 'string' ? Number(protocolVersion) : protocolVersion,
    clientVersion,
  };
}
```

### 3. Normalize mixed-client protocol responses

```python
def normalize_protocol_version(value) -> int:
    if isinstance(value, str):
        return int(value, 0)

    return int(value)
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/bittensor/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/bittensor/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/bittensor/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Bittensor RPC Method

Submits a pre-signed transaction for broadcast to Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Bittensor
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Bittensor mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/bittensor/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Bittensor RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0x156e431cc96e0e3b70c97214d869c9bc4b5bbd21")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/bittensor/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/bittensor/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Bittensor RPC Method

Creates and sends a new transaction from an unlocked account on Bittensor. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for AI/ML developers, subnet operators, and teams building decentralized machine learning applications in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Bittensor RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Bittensor tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Bittensor tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Bittensor tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Bittensor tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Bittensor tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Bittensor dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/bittensor/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/bittensor/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Bittensor RPC Method

Signs a transaction with the private key of the specified account on Bittensor without submitting it to the network.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for AI/ML developers, subnet operators, and teams building decentralized machine learning applications in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Bittensor RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Bittensor tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Bittensor tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Bittensor tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Bittensor:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/bittensor/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/bittensor/eth_accounts) - List accounts available for signing

---

## eth_syncing - Bittensor RPC Method

# eth_syncing - Bittensor RPC Method

Returns the sync status of your Bittensor node - either `false` when fully synced, or an object describing the sync progress.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_syncing` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Bittensor node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Bittensor node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Bittensor node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/bittensor/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/bittensor/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/bittensor/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/bittensor/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Bittensor RPC Method

Removes a filter on Bittensor that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`eth_uninstallFilter` is important for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Bittensor RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/bittensor/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/bittensor/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/bittensor/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/bittensor/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/bittensor/eth_getFilterLogs) - Get all logs matching a filter

---

## grandpa_roundState - Bittensor RPC Method

Returns the state of the current GRANDPA finality round on Bittensor when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`grandpa_roundState` is not currently exposed on Dwellir's public Bittensor RPC. If you need finality status for production monitoring, use [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) for the latest finalized block hash or [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) to stream finalized heads as they arrive.

- **Finalized block tracking** -- Use `chain_getFinalizedHead` to snapshot the current finalized head
- **Realtime finality updates** -- Use `chain_subscribeFinalizedHeads` when your service needs push-based finalized-head updates
- **Operational monitoring** -- Prefer finalized-head health checks over validator-round internals on the shared endpoint

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

```javascript
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1,
  }),
});

const payload = await response.json();
console.log(payload.error);
// { code: -32601, message: 'Method not found' }
```

```python
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'grandpa_roundState',
        'params': [],
        'id': 1,
    },
)

print(response.json()['error'])
# grandpa_roundState - Bittensor RPC Method
```

## Supported Alternatives

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) for the latest finalized block hash
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) for streamed finalized-head updates

## Supported Monitoring Patterns

Because Dwellir's public Bittensor endpoint does not expose `grandpa_roundState`, build finalized-head monitoring around the supported RPCs below.

### 1. Snapshot the latest finalized head

Use `chain_getFinalizedHead` when you want the current finalized block hash on demand:

```javascript
async function getLatestFinalizedHead(api) {
  const finalizedHead = await api.rpc.chain.getFinalizedHead();
  return finalizedHead.toHex();
}
```

### 2. Stream finalized heads in real time

Subscribe to finalized-head updates when your service needs push-based finality signals:

```javascript
async function subscribeFinalizedHeads(api) {
  return api.rpc.chain.subscribeFinalizedHeads((header) => {
    console.log(`Finalized block #${header.number.toString()} (${header.hash.toHex()})`);
  });
}
```

### 3. Measure finalized lag against the best block

Compare the latest finalized head with the newest block header to track confirmation lag:

```javascript
async function getFinalityLag(api) {
  const [finalizedHash, bestHeader] = await Promise.all([
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader(),
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  return {
    bestBlock: bestHeader.number.toNumber(),
    finalizedBlock: finalizedHeader.number.toNumber(),
    lagBlocks: bestHeader.number.toNumber() - finalizedHeader.number.toNumber(),
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/bittensor/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/bittensor/system_health) -- Check overall node health including sync and peer status

---

## net_listening - Bittensor RPC Method

Checks whether the connected Bittensor client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`net_listening` is useful for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Bittensor node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Bittensor node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Bittensor RPC Method
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Bittensor node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/bittensor/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/bittensor/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/bittensor/web3_clientVersion) - Get node client info

---

## net_peerCount - Bittensor RPC Method

Returns the number of peers currently connected to your Bittensor node.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`net_peerCount` is important for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Bittensor deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Bittensor peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Bittensor peers: {peer_count}')

# net_peerCount - Bittensor RPC Method
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Bittensor peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Bittensor nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/bittensor/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/bittensor/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/bittensor/web3_clientVersion) - Get node client info

---

## net_version - Bittensor RPC Method

Returns the current network ID on Bittensor as a decimal string. The network ID identifies which network the node is connected to.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`net_version` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Endpoint Identification** - Confirm your application is connected to the expected Bittensor network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Bittensor network ID:', networkId);

// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Bittensor network ID: {network_id}')

# net_version - Bittensor RPC Method
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Bittensor network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/bittensor/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/bittensor/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/bittensor/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check node sync progress

---

## neuronInfo_getNeuron - Bittensor RPC Method

# neuronInfo_getNeuron - Bittensor RPC Method

## Overview

The `neuronInfo_getNeuron` method returns SCALE-encoded detailed information for a specific neuron identified by its UID within a Bittensor subnet. Every participant in a subnet -- whether a validator or a miner -- is a "neuron" with a unique UID assigned at registration time.

This method returns comprehensive data about a single neuron: its identity (hotkey/coldkey), stake, performance scores (trust, consensus, incentive, dividends), emission earnings, axon endpoint, and weight/bond information. It is the most detailed single-neuron query available.

Use this method when you need to inspect a specific neuron's state -- for example, after looking up a UID in the metagraph and wanting the full detail view.

## SCALE Decoding

The response decodes to a `NeuronInfo` struct using Bittensor's custom type registry.

**Using `@polkadot/api`:** Register the following types:

- `NeuronInfo`: The main neuron struct with all fields listed above
- `AxonInfo`: Contains `version` (u32), `ip` (u128), `port` (u16), `ip_type` (u8), `protocol` (u8), `placeholder1` (u8), `placeholder2` (u8)
- `PrometheusInfo`: Contains `version` (u32), `ip` (u128), `port` (u16), `ip_type` (u8)

**Using `bittensor` Python SDK:** Use `sub.neuron_for_uid(uid=N, netuid=M)` which returns a `NeuronInfo` namedtuple with all fields decoded and converted to human-readable formats.

**Key decoding notes:**

- All u16 score fields are scaled 0--65535. Divide by 65535 for float values (0.0--1.0)
- The `stake` field is a vector of staking entries. Sum the decoded amounts if you need a total stake figure.
- `emission` is in RAO per tempo. To get TAO per day: `(emission / 1e9) * (7200 / tempo)` where tempo is the subnet's block interval
- IP addresses in `axon_info` and `prometheus_info` are stored as u128. For IPv4, convert the 4 least significant bytes
- `weights` is a sparse vector: only non-zero weight assignments are included

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`u16, required`): The subnet identifier
- `uid` (`u16, required`): The neuron's unique identifier within the subnet
- `at` (`BlockHash, optional`): Optional block hash to query at a specific block. Pass `null` for the latest state

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "neuronInfo_getNeuron",
    "params": [1, 0, null],
    "id": 1
  }'
```

## Response Fields

- `hotkey` (`AccountId32, required`): The neuron's hotkey address (identifies the neuron on-chain)
- `coldkey` (`AccountId32, required`): The neuron's coldkey (owner) address
- `uid` (`u16, required`): Neuron UID within the subnet
- `netuid` (`u16, required`): The subnet identifier
- `active` (`bool, required`): Whether the neuron is currently active
- `stake` (`Vec<(AccountId32, Compact<u64>)>, required`): Stake entries associated with the neuron on the queried subnet
- `rank` (`u16, required`): Neuron's rank score (0--65535 scaled)
- `emission` (`u64, required`): Emissions earned per tempo (RAO). Divide by 1e9 for TAO
- `incentive` (`u16, required`): Incentive score (0--65535 scaled). Higher = better-performing miner
- `consensus` (`u16, required`): Consensus score (0--65535 scaled). Reflects agreement among validators
- `trust` (`u16, required`): Trust score (0--65535 scaled). Measures how much other neurons trust this one
- `validator_trust` (`u16, required`): Validator-specific trust score (how much other validators trust this one)
- `dividends` (`u16, required`): Dividend score (0--65535 scaled). Higher = more validator reward share
- `last_update` (`u64, required`): Last block this neuron set weights
- `validator_permit` (`bool, required`): Whether this neuron has a validator permit
- `weights` (`Vec<(u16, u16)>, required`): Weight assignments to other neurons (target_uid, weight)
- `bonds` (`Vec<(u16, u16)>, required`): Bond assignments (target_uid, bond_amount)
- `axon_info` (`AxonInfo, required`): Network endpoint (IP, port, protocol version)
- `prometheus_info` (`PrometheusInfo, required`): Prometheus metrics endpoint (IP, port)
- `pruning_score` (`u16, required`): Score used to determine deregistration priority (lower = more likely to be pruned)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hotkey": "<value>",
    "coldkey": "<value>",
    "uid": "<value>",
    "netuid": "<value>",
    "active": true,
    "stake": "<value>",
    "rank": "<value>",
    "emission": "<value>",
    "incentive": "<value>",
    "consensus": "<value>",
    "trust": "<value>",
    "validator_trust": "<value>",
    "dividends": "<value>",
    "last_update": "<value>",
    "validator_permit": true,
    "weights": "<value>",
    "bonds": "<value>",
    "axon_info": "<value>",
    "prometheus_info": "<value>",
    "pruning_score": "<value>"
  }
}
```

## Error Responses

### Error 1

### Error 2

### Error 3

### Error 4

### Error 5

### Error 6

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch neuron info for UID 0 in subnet 1
payload = {
    'jsonrpc': '2.0',
    'method': 'neuronInfo_getNeuron',
    'params': [1, 0, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Neuron info SCALE data size: {len(scale_bytes)} bytes')
else:
    print('No neuron found at this UID')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# neuron = sub.neuron_for_uid(uid=0, netuid=1)
# print(f"Hotkey: {neuron.hotkey}")
# print(f"Stake: {neuron.stake:.4f} TAO")
# print(f"Trust: {neuron.trust:.4f}")
# print(f"Incentive: {neuron.incentive:.4f}")
# print(f"Emission: {neuron.emission:.4f} TAO/tempo")
```

### Full Python neuron analysis

```python
import bittensor as bt
import socket
import struct

sub = bt.subtensor(network='finney')

# Detailed neuron inspection
netuid = 1
uid = 0
neuron = sub.neuron_for_uid(uid=uid, netuid=netuid)

print(f"=== Neuron UID {uid} on Subnet {netuid} ===")
print(f"Hotkey:  {neuron.hotkey}")
print(f"Coldkey: {neuron.coldkey}")
print(f"Active:  {neuron.active}")

# Role identification
role = "Validator" if neuron.validator_permit else "Miner"
print(f"Role:    {role}")

# Performance scores
print(f"\n--- Performance ---")
print(f"Trust:           {neuron.trust:.4f}")
print(f"Consensus:       {neuron.consensus:.4f}")
print(f"Incentive:       {neuron.incentive:.4f}")
print(f"Dividends:       {neuron.dividends:.4f}")
print(f"Rank:            {neuron.rank:.4f}")
print(f"Validator Trust: {neuron.validator_trust:.4f}")
print(f"Pruning Score:   {neuron.pruning_score:.4f}")

# Stake breakdown
print(f"\n--- Stake ---")
total_stake = sum(amount.tao for _, amount in neuron.stake)
print(f"Total stake: {total_stake:,.2f} TAO ({len(neuron.stake)} stake entries)")
for coldkey, amount in sorted(neuron.stake, key=lambda x: x[1], reverse=True)[:5]:
    pct = (amount.tao / total_stake * 100) if total_stake > 0 else 0
    print(f"  {coldkey[:18]}.. : {amount.tao:>12,.2f} TAO ({pct:.1f}%)")

# Emissions
print(f"\n--- Emissions ---")
print(f"Emission per tempo: {neuron.emission:.6f} TAO")

# Weight analysis (for validators)
if neuron.validator_permit and neuron.weights:
    print(f"\n--- Weights (top 10) ---")
    sorted_weights = sorted(neuron.weights, key=lambda x: x[1], reverse=True)
    for target_uid, weight in sorted_weights[:10]:
        print(f"  -> UID {target_uid}: weight={weight / 65535:.4f}")

# Network endpoint
if neuron.axon_info.ip != 0:
    ip_bytes = neuron.axon_info.ip.to_bytes(16, 'big')
    ip_str = socket.inet_ntoa(ip_bytes[-4:])
    print(f"\n--- Axon Endpoint ---")
    print(f"IP: {ip_str}:{neuron.axon_info.port}")
    print(f"Version: {neuron.axon_info.version}")

# Last activity
current_block = sub.block
blocks_since_update = current_block - neuron.last_update
minutes_since = blocks_since_update * 12 / 60
print(f"\n--- Activity ---")
print(f"Last weight update: block {neuron.last_update} ({blocks_since_update} blocks / ~{minutes_since:.0f} min ago)")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Fetch neuron info for UID 0 in subnet 1
const neuronInfo = await api.rpc.neuronInfo.getNeuron(1, 0);
console.log('Raw data size:', neuronInfo.toHex().length, 'bytes');

// With Bittensor types registered:
// console.log('Hotkey:', neuronInfo.hotkey.toHuman());
// console.log('Active:', neuronInfo.active.toString());
// console.log('Emission:', neuronInfo.emission.toString());
// console.log('Validator permit:', neuronInfo.validator_permit.toString());

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "neuronInfo_getNeuron",
    "params": [1, 0, null],
    "id": 1
  }'
```

### Historical performance tracking

```python
import requests

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Query neuron state at multiple past blocks to track performance
netuid = 1
uid = 42
block_hashes = ['0xabc...', '0xdef...', '0x123...']  # Past block hashes

for i, block_hash in enumerate(block_hashes):
    payload = {
        'jsonrpc': '2.0',
        'method': 'neuronInfo_getNeuron',
        'params': [netuid, uid, block_hash],
        'id': i + 1
    }
    response = requests.post(url, json=payload)
    result = response.json()
    if result.get('result'):
        print(f"Block {block_hash[:10]}...: {len(result['result']) // 2} bytes")
    # Decode each snapshot to track score changes over time
```

ze via `subnetInfo_getMetagraph` before querying |
\| Deregistered neuron | Decodes to a low-signal or placeholder neuron payload depending on runtime tooling | Cross-check the UID against the current metagraph before treating the record as active |
\| Invalid block hash | Returns JSON-RPC error | Verify block hash exists |
\| Node not synced | May return stale neuron data | Check `system_health` |
\| Rate limit exceeded | HTTP 429 | Cache neuron data; refresh once per tempo |

## Common Use Cases

- **Miner monitoring** — Track a miner's incentive score, rank, emission earnings, and active status over time.
- **Validator monitoring** — Monitor a validator's trust, consensus scores, and weight-setting frequency.
- **Performance tracking** — Compare a neuron's scores across blocks to detect performance trends or anomalies.
- **Axon discovery** — Look up a neuron's network endpoint (IP/port) for direct communication.
- **Staking decisions** — Inspect a neuron's stake distribution, validator permit status, and trust before delegating.
- **Explorer views** — Build neuron detail pages showing all attributes for a specific participant.
- **Pruning risk assessment** — Monitor the pruning score to anticipate potential deregistration.
- **Weight auditing** — Inspect a validator's weight assignments to verify they are evaluating miners correctly.

## Related Methods

- [`neuronInfo_getNeurons`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeurons) — Get detailed info for all neurons in a subnet
- [`neuronInfo_getNeuronLite`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuronLite) — Get lightweight neuron info (fewer fields, smaller payload)
- [`neuronInfo_getNeuronsLite`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuronsLite) — Get lightweight info for all neurons
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the full metagraph for a subnet
- [`subnetInfo_getSelectiveMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMetagraph) — Get metagraph data for specific UIDs
- [`delegateInfo_getDelegate`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegate) — Get delegate info if this neuron is a delegate

---

## neuronInfo_getNeuronLite - JSON-RPC Method

# neuronInfo_getNeuronLite - JSON-RPC Method

## Description

Bittensor neuron information (lite and detailed) per subnet, including UIDs and attributes. Use to index participants and build explorer views.

Returns compact neuron info for a single neuron (uid) in a subnet.

## Code Examples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `uid` (`INTEGER, required`): Neuron UID within the subnet.
- `at` (`DATA, optional`): Optional block hash to query state at.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "neuronInfo_getNeuronLite",
    "params": [1, 0, null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded lite neuron info.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x11223344"
}
```

---

## neuronInfo_getNeurons - JSON-RPC Method

# neuronInfo_getNeurons - JSON-RPC Method

## Description

Bittensor neuron information (lite and detailed) per subnet, including UIDs and attributes. Use to index participants and build explorer views.

Returns detailed neuron info for all neurons in a subnet.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical neuron state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "neuronInfo_getNeurons",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded neuron records for the requested subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    4,
    184,
    125
  ]
}
```

---

## neuronInfo_getNeuronsLite - JSON-RPC Method

# neuronInfo_getNeuronsLite - JSON-RPC Method

## Description

Bittensor neuron information (lite and detailed) per subnet, including UIDs and attributes. Use to index participants and build explorer views.

Returns compact neuron info for all neurons in a subnet.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical neuron state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "neuronInfo_getNeuronsLite",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded compact neuron data for the requested subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    4,
    184,
    125
  ]
}
```

---

## offchain_localStorageClear - Bittensor RPC Method

# offchain_localStorageClear - Bittensor RPC Method

Removes a key from the node's offchain local storage. Offchain local storage is a node-local key-value store used by offchain workers and is not shared across the network or stored on-chain. This is an administrative method that requires the node to be running with `--rpc-methods unsafe` and is not available on public shared RPC endpoints.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "offchain_localStorageClear",
    "params": [],
    "id": 1
  }'
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Offchain worker reset** -- Clear cached or stale offchain data that an offchain worker relies on, forcing it to re-fetch from external sources.
- **Development and testing** -- Reset offchain state during development or debugging of offchain worker logic.
- **Storage cleanup** -- Remove obsolete entries from offchain storage on a self-hosted Bittensor node.

## Notes

- This is an unsafe/administrative method. It is disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- Offchain storage is local to each node and is not part of the blockchain state.
- The `PERSISTENT` kind survives node restarts; `LOCAL` kind is cleared when the node restarts.

## Related Methods

- [`offchain_localStorageGet`](https://www.dwellir.com/docs/bittensor/offchain_localStorageGet) -- Read a value from offchain local storage
- [`offchain_localStorageSet`](https://www.dwellir.com/docs/bittensor/offchain_localStorageSet) -- Write a value to offchain local storage

---

## offchain_localStorageGet - Bittensor RPC Method

# offchain_localStorageGet - Bittensor RPC Method

Reads a value from the node's offchain local storage by key. Offchain local storage is a node-local key-value store used by offchain workers for caching data, tracking state, or communicating between offchain worker runs. This data is not stored on-chain and is specific to each node.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "offchain_localStorageGet",
    "params": [],
    "id": 1
  }'
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Offchain worker debugging** -- Inspect values stored by offchain workers to verify correct behavior.
- **State inspection** -- Read cached external data (e.g. oracle prices, API responses) that offchain workers have stored locally.
- **Monitoring** -- Check whether offchain workers are running and updating their local state.

## Notes

- On public RPC endpoints (including Dwellir's shared Bittensor nodes), offchain storage access is usually disabled. Expect `null` or an error.
- Offchain storage is local to each node. Two nodes running the same chain will have different offchain storage contents.
- The `PERSISTENT` kind survives node restarts; `LOCAL` kind is ephemeral.

## Related Methods

- [`offchain_localStorageSet`](https://www.dwellir.com/docs/bittensor/offchain_localStorageSet) -- Write a value to offchain local storage
- [`offchain_localStorageClear`](https://www.dwellir.com/docs/bittensor/offchain_localStorageClear) -- Remove a key from offchain local storage

---

## offchain_localStorageSet - Bittensor RPC Method

# offchain_localStorageSet - Bittensor RPC Method

Writes a key-value pair to the node's offchain local storage. Offchain local storage is a node-local key-value store used by offchain workers and is not shared across the network or stored on-chain. This is an administrative method that requires the node to be running with `--rpc-methods unsafe`.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "offchain_localStorageSet",
    "params": [],
    "id": 1
  }'
```

## Response Fields

This method returns no documented fields.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {}
}
```

## Use Cases

- **Offchain worker configuration** -- Pre-load configuration or seed data for offchain workers on a self-hosted Bittensor node.
- **Testing and development** -- Inject test data into offchain storage for developing and debugging offchain worker logic.
- **External data caching** -- Store external API responses or oracle data that offchain workers can use in subsequent runs.

## Notes

- This is an unsafe/administrative method. It is disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- In live checks on Dwellir-hosted public Bittensor endpoints, the call returns `-32601 RPC call is unsafe to be called externally`.
- Offchain storage is local to each node and is not replicated across the network.
- The `PERSISTENT` kind survives node restarts; `LOCAL` kind is cleared when the node restarts.
- Both key and value must be hex-encoded with a `0x` prefix.

## Related Methods

- [`offchain_localStorageGet`](https://www.dwellir.com/docs/bittensor/offchain_localStorageGet) -- Read a value from offchain local storage
- [`offchain_localStorageClear`](https://www.dwellir.com/docs/bittensor/offchain_localStorageClear) -- Remove a key from offchain local storage

---

## payment_queryFeeDetails - Bittensor RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Bittensor. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`payment_queryFeeDetails` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Bittensor
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/bittensor/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Bittensor RPC Method

Estimates the fee for an encoded extrinsic on Bittensor. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`payment_queryInfo` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Bittensor
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Bittensor RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/bittensor/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Bittensor RPC Method

Returns a list of all RPC methods exposed by the Bittensor node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`rpc_methods` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Bittensor node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Bittensor endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Bittensor endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bittensor/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Bittensor RPC Method

Calls a runtime API function on Bittensor and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_call` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/bittensor/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node version for compatibility checking

---

## state_callAt - Bittensor RPC Method

# state_callAt - Bittensor RPC Method

Executes a runtime API function with SCALE-encoded parameters at a specific block hash. This is a variant of `state_call` that requires an explicit block hash, making it useful for historical queries. You can invoke any runtime API function exposed by the Bittensor runtime, such as `Metadata_metadata`, `AccountNonceApi_account_nonce`, or `TransactionPaymentApi_query_info`.

## Code Examples

## Request Parameters

- `method` (`string, required`): Runtime API function name (e.g. `"Metadata_metadata"`, `"AccountNonceApi_account_nonce"`).
- `data` (`string, required`): Hex-encoded SCALE-encoded call parameters. Use `"0x"` for functions that take no arguments.
- `blockHash` (`string, required`): Hex-encoded block hash to execute the call against.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_callAt",
  "params": [
    "<method>",
    "<data>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Hex-encoded SCALE-encoded return value. Must be decoded according to the runtime API's return type.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Historical metadata** -- Retrieve the runtime metadata as it existed at a past block to correctly decode storage and extrinsics from that era.
- **Fee estimation at past blocks** -- Call `TransactionPaymentApi_query_info` at historical blocks to analyze fee trends over time.
- **Cross-version analysis** -- Compare runtime API outputs across different blocks to track how Bittensor subnet parameters or staking rules evolved.

## Notes

- The `data` parameter must be SCALE-encoded. Use a library such as `@polkadot/types` or `parity-scale-codec` to encode input and decode output.
- Requires an archive node for historical block hashes. Non-archive nodes only retain recent state.
- This method is equivalent to `state_call` with an explicit block hash parameter.

## Related Methods

- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Runtime API call at the latest or a specified block
- [`archive_v1_call`](https://www.dwellir.com/docs/bittensor/archive_v1_call) -- Runtime call via the new JSON-RPC v2 archive API
- [`chainHead_v1_call`](https://www.dwellir.com/docs/bittensor/chainHead_v1_call) -- Runtime call within a follow subscription
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Shortcut for fetching runtime metadata

---

## state_getChildReadProof - Bittensor RPC Method

# state_getChildReadProof - Bittensor RPC Method

Returns a Merkle proof for one or more keys in a child storage trie at a given block. The proof can be used to verify the values of those keys without trusting the RPC provider -- the verifier only needs the state root from a trusted block header. This is the child-trie equivalent of `state_getReadProof`.

## Code Examples

## Request Parameters

- `childStorageKey` (`string, required`): Hex-encoded key identifying the child trie root.
- `keys` (`array, required`): Array of hex-encoded storage keys within the child trie to prove.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getChildReadProof",
  "params": [
    "<childStorageKey>",
    "<keys>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `at` (`string, required`): Block hash at which the proof was generated.
- `proof` (`array, required`): Array of hex-encoded trie nodes forming the Merkle proof.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "at": "<value>",
    "proof": []
  }
}
```

## Use Cases

- **Trust-minimized verification** -- Verify child storage values off-chain using only the state root from a trusted block header, without trusting the RPC provider.
- **Bridge proofs** -- Generate proofs for cross-chain bridges that need to verify Bittensor child storage state on another chain.
- **Light client support** -- Provide state proofs to light clients that do not store the full state trie.

## Notes

- Child trie usage is runtime-specific. Not all Bittensor pallets use child storage, so this method may return empty proofs for many queries.
- Replace the example parameters with actual child storage keys from the runtime you are querying.
- Requires an archive node for historical block hashes.

## Related Methods

- [`state_getReadProof`](https://www.dwellir.com/docs/bittensor/state_getReadProof) -- Generate Merkle proofs for main-trie storage keys
- [`childstate_getStorage`](https://www.dwellir.com/docs/bittensor/childstate_getStorage) -- Read child storage values (without proofs)
- [`childstate_getStorageEntries`](https://www.dwellir.com/docs/bittensor/childstate_getStorageEntries) -- Batch read child storage values
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read main-trie storage values

---

## state_getKeys - Bittensor RPC Method

# state_getKeys - Bittensor RPC Method

Returns all storage keys matching a given prefix. This method loads all matching keys into memory at once, which can be problematic for large storage maps. It is deprecated in favor of `state_getKeysPaged`, which supports cursor-based pagination and is safer for production use.

> **Deprecated:** Use [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) instead. This method may cause memory issues or timeouts on large storage maps.

## Code Examples

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage key prefix. Use the pallet's key prefix to list all keys in a storage map.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "<prefix>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of all hex-encoded storage keys matching the prefix.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Small storage maps** -- Enumerate all keys in a storage map that you know to be small (e.g. a map with fewer than 1000 entries).
- **Quick exploration** -- Inspect which keys exist under a prefix during development or debugging.

## Notes

- This method returns all matching keys in a single response. For large maps (like `System.Account` on Bittensor), this can cause memory exhaustion or timeouts.
- Always prefer `state_getKeysPaged` for production code. It returns keys in pages and handles large maps safely.
- Requires an archive node for historical block hashes.

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Paginated key enumeration (recommended replacement)
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read the value for a specific key
- [`state_getPairs`](https://www.dwellir.com/docs/bittensor/state_getPairs) -- Get key-value pairs for a prefix (also deprecated)
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getKeysPaged - Bittensor RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Bittensor. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_getKeysPaged` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Bittensor
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getKeysPagedAt - JSON-RPC Method

# state_getKeysPagedAt - JSON-RPC Method

## Description

Enumerate storage keys for a prefix. Use for pagination when exploring large maps, then query values with state\_getStorage.

Returns storage keys with a given prefix, paginated, at a specific block hash.

## Code Examples

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage prefix to enumerate.
- `count` (`number, required`): Maximum number of keys to return in this page.
- `startKey` (`string | null, optional`): Optional cursor key from the previous page.
- `blockHash` (`string, required`): Hex-encoded block hash to read the state at.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPagedAt",
  "params": [
    "0x3a636f6465",
    10,
    null,
    "0x1e8a700fa840157d8d5617eac90ecd3b795d6469ddb8a9ec7dd2d051d806e85d"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array<string>, required`): Hex-encoded storage keys matching the prefix at the requested block hash.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x3a636f6465"
  ]
}
```

---

## state_getMetadata - Bittensor RPC Method

Returns the runtime metadata for Bittensor as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_getMetadata` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Bittensor
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Bittensor RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Bittensor:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Bittensor:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) - Call runtime APIs described in metadata

---

## state_getPairs - Bittensor RPC Method

# state_getPairs - Bittensor RPC Method

Returns all storage key-value pairs matching a given prefix. Each entry in the result contains both the full storage key and its SCALE-encoded value. Like `state_getKeys`, this method loads all matching pairs into memory at once and can be problematic for large storage maps.

> **Note:** For large storage maps, consider using `state_getKeysPaged` to enumerate keys and then `state_getStorage` or `state_queryStorageAt` to fetch values in batches.

## Code Examples

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage key prefix.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getPairs",
  "params": [
    "<prefix>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of `[key, value]` tuples where both are hex-encoded strings. Values are SCALE-encoded.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Small map export** -- Dump all entries from a storage map that is known to be small for analysis or migration.
- **Development and debugging** -- Quickly inspect the contents of a storage prefix during development.
- **State snapshots** -- Export a specific subset of on-chain state for offline analysis of Bittensor subnet data, staking maps, or other pallet storage.

## Notes

- Returns all matching pairs in a single response. For large maps this can cause memory exhaustion or timeouts.
- Values are SCALE-encoded. Decode them using the type information from runtime metadata.
- For production use with large maps, prefer paginated approaches: enumerate keys with `state_getKeysPaged`, then batch-fetch values with `state_queryStorageAt`.
- Requires an archive node for historical block hashes.

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Paginated key enumeration (safer for large maps)
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read a single storage value by key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Batch-read multiple storage values at a block
- [`state_getKeys`](https://www.dwellir.com/docs/bittensor/state_getKeys) -- Get keys only (without values), also deprecated for large maps

---

## state_getReadProof - Bittensor RPC Method

# state_getReadProof - Bittensor RPC Method

Returns a Merkle proof for one or more storage keys at a given block. The proof consists of trie nodes that, together with the state root from a trusted block header, allow a verifier to confirm the values of the specified keys without trusting the RPC provider. This is essential for light clients, cross-chain bridges, and trust-minimized applications.

## Code Examples

## Request Parameters

- `keys` (`array, required`): Array of hex-encoded storage keys to generate proofs for.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getReadProof",
  "params": [
    "<keys>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `at` (`string, required`): Block hash at which the proof was generated.
- `proof` (`array, required`): Array of hex-encoded trie nodes forming the Merkle proof.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "at": "<value>",
    "proof": []
  }
}
```

## Use Cases

- **Trust-minimized verification** -- Verify storage values off-chain using only the state root from a trusted (finalized) block header.
- **Cross-chain bridges** -- Generate proofs that another chain can verify to confirm Bittensor on-chain state (e.g. account balances, subnet parameters).
- **Light clients** -- Provide state proofs to light clients that verify on-chain data without storing the full state trie.
- **Audit and compliance** -- Prove that specific on-chain values existed at a given block without requiring the verifier to run a full node.

## Notes

- The proof is generated against the state trie at the specified block. The verifier needs the `stateRoot` from that block's header.
- Proof verification can be performed using the `@polkadot/trie-hash` or `sp-trie` libraries.
- Requires an archive node for proofs at historical block hashes.

## Related Methods

- [`state_getChildReadProof`](https://www.dwellir.com/docs/bittensor/state_getChildReadProof) -- Generate proofs for child storage keys
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read storage values (without proofs)
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) -- Get the block header containing the state root
- [`state_getStorageAt`](https://www.dwellir.com/docs/bittensor/state_getStorageAt) -- Read storage at a specific block

---

## state_getRuntimeVersion - Bittensor RPC Method

# state_getRuntimeVersion - Bittensor RPC Method

Returns the runtime version information for Bittensor, including the spec name, spec version, implementation version, and supported API versions.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_getRuntimeVersion` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Bittensor
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Bittensor in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Bittensor RPC Method

Returns the SCALE-encoded storage value for a given key on Bittensor. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_getStorage` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Bittensor
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Bittensor RPC Method
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call runtime APIs for computed state that is not directly in storage
- [`state_subscribeStorage`](https://www.dwellir.com/docs/bittensor/state_subscribeStorage) -- Subscribe to storage changes in real time via WebSocket

---

## state_getStorageAt - Bittensor RPC Method

# state_getStorageAt - Bittensor RPC Method

Returns the SCALE-encoded storage value for a given key at a specific block hash. This is equivalent to `state_getStorage` with an explicit block hash parameter. Use it to read historical on-chain state at any past block on Bittensor, enabling time-travel queries for analytics, auditing, and debugging.

## Code Examples

## Request Parameters

- `key` (`string, required`): Hex-encoded storage key to read.
- `blockHash` (`string, required`): Hex-encoded block hash to read the storage at.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorageAt",
  "params": [
    "<key>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string` or `null, required`): Hex-encoded SCALE-encoded value, or `null` if the key does not exist at the given block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Historical balance queries** -- Read account balances, staking positions, or subnet parameters as they existed at a specific block.
- **Analytics and reporting** -- Build time-series data by reading the same storage key across a range of block hashes.
- **Debugging** -- Verify what on-chain state looked like at the block where an issue occurred.
- **Audit trails** -- Prove that a specific value existed on-chain at a given point in time.

## Notes

- Requires an archive node. Non-archive nodes only keep recent state (typically the last 256 blocks).
- The returned value is SCALE-encoded. Decode it using the type information from the runtime metadata at the same block.
- The example above uses the well-known `:code` key (`0x3a636f6465`) at a real Bittensor block hash. Replace it with the storage key you actually want to inspect.
- To determine the correct storage key for pallet state, use `state_getMetadata` and the Substrate storage key hashing scheme.

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read storage at the latest block (or optionally at a given hash)
- [`state_getStorageHash`](https://www.dwellir.com/docs/bittensor/state_getStorageHash) -- Get the hash of a storage value instead of the full value
- [`state_getStorageSize`](https://www.dwellir.com/docs/bittensor/state_getStorageSize) -- Get the byte size of a storage value
- [`state_queryStorage`](https://www.dwellir.com/docs/bittensor/state_queryStorage) -- Query storage changes across a range of blocks
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Enumerate storage keys to discover what to query

---

## state_getStorageHash - Bittensor RPC Method

# state_getStorageHash - Bittensor RPC Method

Returns the hash (blake2b-256) of the storage value for a given key at the best block (or at a specific block hash if provided). This is a lightweight alternative to `state_getStorage` when you only need to detect whether a value has changed, without downloading the full value.

## Code Examples

## Request Parameters

- `key` (`string, required`): Hex-encoded storage key.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorageHash",
  "params": [
    "<key>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string` or `null, required`): Hex-encoded blake2b-256 hash of the storage value, or `null` if the key does not exist.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Change detection** -- Compare storage hashes across blocks to determine whether a value has changed without downloading the full (potentially large) value.
- **Caching optimization** -- Cache storage values locally and use the hash to determine if the cache is stale, avoiding unnecessary bandwidth for large values like the Wasm runtime blob.
- **Integrity verification** -- Confirm that a locally stored value matches the on-chain value by comparing hashes.

## Notes

- The hash is computed over the raw SCALE-encoded storage value.
- Returns `null` for non-existent keys, which is distinct from a key that exists with an empty value.
- Requires an archive node for historical block hashes.

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read the full storage value
- [`state_getStorageAt`](https://www.dwellir.com/docs/bittensor/state_getStorageAt) -- Read storage at a specific block hash
- [`state_getStorageSize`](https://www.dwellir.com/docs/bittensor/state_getStorageSize) -- Get the byte size of a storage value
- [`state_queryStorage`](https://www.dwellir.com/docs/bittensor/state_queryStorage) -- Track storage changes over a block range

---

## state_getStorageHashAt - JSON-RPC Method

# state_getStorageHashAt - JSON-RPC Method

## Description

Read SCALE‑encoded storage for a key (optionally at a specific block). Use to fetch on‑chain state deterministically and to implement historical reads.

Returns the storage hash for a key at the given block hash.

## Code Examples

## Request Parameters

- `key` (`string, required`): Hex-encoded storage key to hash.
- `blockHash` (`string, required`): Hex-encoded block hash to read the storage at.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorageHashAt",
  "params": [
    "0x3a636f6465",
    "0x1e8a700fa840157d8d5617eac90ecd3b795d6469ddb8a9ec7dd2d051d806e85d"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`string | null, required`): Hex-encoded hash of the storage value at the requested block, or `null` when the key is absent.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x84aea08073c027fa3c5ecf0079b6beec969f8afc5558329b18616949ceb257af"
}
```

---

## state_getStorageSize - Bittensor RPC Method

# state_getStorageSize - Bittensor RPC Method

Returns the size in bytes of the SCALE-encoded storage value for a given key at the best block (or at a specified block hash). This is useful for checking whether a key exists and estimating the bandwidth needed to fetch its full value without actually downloading it.

ze in bytes of the storage value, or `null` if the key does not exist. |

## Code Examples

## Request Parameters

- `key` (`string, required`): Hex-encoded storage key.
- `blockHash` (`string, optional`): Hex-encoded block hash. Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorageSize",
  "params": [
    "<key>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`number` or `null, required`): Size in bytes of the storage value, or `null` if the key does not exist.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Use Cases

- **Existence check** -- Determine if a storage key exists without fetching the value (returns `null` for non-existent keys).
- **Bandwidth estimation** -- Estimate how much data will be transferred when reading a storage value, useful for planning batch reads.
- **Runtime code size** -- Check the size of the Wasm runtime blob stored under `:code` without downloading it.
- **Storage monitoring** -- Track the size of specific storage entries over time to monitor chain growth.

## Notes

- Returns the size of the raw SCALE-encoded value, not the decoded value.
- Returns `null` for non-existent keys rather than 0.
- Requires an archive node for historical block hashes.

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read the full storage value
- [`state_getStorageHash`](https://www.dwellir.com/docs/bittensor/state_getStorageHash) -- Get the hash of a storage value
- [`state_getStorageAt`](https://www.dwellir.com/docs/bittensor/state_getStorageAt) -- Read storage at a specific block hash
- [`childstate_getStorageSize`](https://www.dwellir.com/docs/bittensor/childstate_getStorageSize) -- Get size of child trie storage values

---

## state_getStorageSizeAt - JSON-RPC Method

# state_getStorageSizeAt - JSON-RPC Method

## Description

Read SCALE‑encoded storage for a key (optionally at a specific block). Use to fetch on‑chain state deterministically and to implement historical reads.

Returns the size (in bytes) of the storage value for the key at the given block.

## Code Examples

## Request Parameters

- `key` (`string, required`): Hex-encoded storage key to measure.
- `blockHash` (`string, required`): Hex-encoded block hash to read the storage at.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorageSizeAt",
  "params": [
    "0x3a636f6465",
    "0x1e8a700fa840157d8d5617eac90ecd3b795d6469ddb8a9ec7dd2d051d806e85d"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`number, required`): Byte length of the stored SCALE value at the requested block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 1659177
}
```

---

## state_queryStorage - Bittensor RPC Method

# state_queryStorage - Bittensor RPC Method

Returns the storage changes for the specified keys over a range of blocks. For each block in the range where at least one of the queried keys changed, the method returns the block hash and the new values. This is useful for tracking how specific storage entries evolved over time without having to query each block individually.

## Code Examples

## Request Parameters

- `keys` (`array, required`): Array of hex-encoded storage keys to track.
- `fromBlock` (`string, required`): Hex-encoded hash of the starting block (inclusive).
- `toBlock` (`string, optional`): Hex-encoded hash of the ending block (inclusive). Defaults to the best block if omitted.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorage",
  "params": [
    "<keys>",
    "<fromBlock>",
    "<toBlock>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of change sets. Each entry has `block` (block hash) and `changes` (array of `[key, value]` pairs).

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Balance history** -- Track how an account balance changed over a range of blocks.
- **Parameter tracking** -- Monitor changes to Bittensor subnet parameters, staking rates, or other governance-controlled values over time.
- **Event-driven indexing** -- Detect which blocks contained changes to specific storage entries, then process only those blocks.
- **Debugging** -- Identify exactly when a storage value changed during incident investigation.

## Notes

- This method can be expensive for large block ranges. The node must replay each block in the range to detect changes. Consider using narrow ranges.
- For single-block queries, prefer `state_queryStorageAt` which is optimized for that case.
- Only blocks where at least one queried key changed are included in the result.
- Requires an archive node for historical block ranges.
- On Dwellir shared public Bittensor endpoints this range query is currently treated as unsafe. Expect `RPC call is unsafe to be called externally` unless you run it on infrastructure that exposes the method.

## Related Methods

- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Query storage at a single block (more efficient for point-in-time queries)
- [`state_subscribeStorage`](https://www.dwellir.com/docs/bittensor/state_subscribeStorage) -- Subscribe to live storage changes via WebSocket
- [`state_getStorageAt`](https://www.dwellir.com/docs/bittensor/state_getStorageAt) -- Read a single storage value at a specific block
- [`archive_v1_storageDiff`](https://www.dwellir.com/docs/bittensor/archive_v1_storageDiff) -- v2 API for streaming storage changes between blocks

---

## state_queryStorageAt - Bittensor RPC Method

Queries multiple storage keys at a specific block on Bittensor, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`state_queryStorageAt` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Bittensor
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Bittensor RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bittensor/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/bittensor/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bittensor/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## state_subscribeRuntimeVersion - JSON-RPC M...

# state_subscribeRuntimeVersion - JSON-RPC M...

Subscribes to runtime version updates over a WebSocket connection. Use it to detect Bittensor runtime upgrades so your indexers, signers, and type registries can refresh before decoding new blocks or extrinsics.

The initial JSON-RPC response returns a subscription ID. Runtime version objects are delivered afterward as WebSocket notifications on that subscription.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_subscribeRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`string, required`): Subscription ID returned by the node. Runtime version notifications are delivered afterward on this subscription.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_subscribeRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

### JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const api = await ApiPromise.create({
  provider: new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
});

const unsub = await api.rpc.state.subscribeRuntimeVersion((version) => {
  console.log(`Runtime upgrade detected: specVersion=${version.specVersion}`);
  console.log(`Transaction version: ${version.transactionVersion}`);
});

// Later: unsub();
```

## Use Cases

- **Metadata refresh** -- Detect runtime upgrades and reload metadata before decoding new blocks or extrinsics.
- **Signer safety** -- Watch `transactionVersion` changes that can invalidate previously prepared unsigned transactions.
- **Operational alerting** -- Notify operators or dashboards when Bittensor upgrades on-chain logic.

## Notes

- The first notification typically arrives immediately with the current runtime version.
- Cancel the subscription with `state_unsubscribeRuntimeVersion` when you no longer need updates.
- This subscription is WebSocket-only.

## Related Methods

- [`state_unsubscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_unsubscribeRuntimeVersion) -- Cancel this subscription
- [`chain_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_subscribeRuntimeVersion) -- Equivalent runtime-version subscription via the chain namespace
- [`chain_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_getRuntimeVersion) -- One-shot runtime version query

---

## state_subscribeStorage - Bittensor RPC Method

# state_subscribeStorage - Bittensor RPC Method

Subscribes to storage changes for a set of keys via a WebSocket connection. Each time one or more of the specified keys change in a new block, a notification is emitted with the updated values. This enables real-time reactive applications that respond to on-chain state changes without polling.

The initial JSON-RPC response returns a subscription ID. Storage-change payloads are delivered afterward as WebSocket notifications for that subscription.

## Request Parameters

- `keys` (`array, required`): Array of hex-encoded storage keys to watch.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_subscribeStorage",
    "params": [
      ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9"]
    ],
    "id": 1
  }'
```

## Response Fields

- `block` (`string, required`): Block hash where the changes occurred.
- `changes` (`array, required`): Array of `[key, value]` pairs for keys that changed. Values are hex-encoded SCALE or `null` if deleted.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_subscribeStorage",
    "params": [
      ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9"]
    ],
    "id": 1
  }'
```

### JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const api = await ApiPromise.create({
  provider: new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
});

const unsub = await api.rpc.state.subscribeStorage(
  ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'],
  (changes) => {
    console.log('Storage changed:', changes.toHuman());
  }
);

// Later: unsub();
```

## Use Cases

- **Live balance tracking** -- Watch account balance changes in real time for wallet UIs or notification systems.
- **Parameter monitoring** -- Get instant notifications when Bittensor subnet parameters, staking configurations, or governance values change.
- **Event-driven processing** -- Trigger downstream actions (alerts, database updates, API calls) when specific on-chain state changes.

## Notes

- Cancel the subscription with `state_unsubscribeStorage` when no longer needed to free server resources.
- The initial notification includes the current values of all watched keys.
- This is a WebSocket-only method; not available over HTTP.

## Related Methods

- [`state_unsubscribeStorage`](https://www.dwellir.com/docs/bittensor/state_unsubscribeStorage) -- Cancel this subscription
- [`state_queryStorage`](https://www.dwellir.com/docs/bittensor/state_queryStorage) -- Query historical storage changes over a block range
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- Point-in-time storage query
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Subscribe to new block headers

---

## state_traceBlock - JSON-RPC Method

# state_traceBlock - JSON-RPC Method

Traces a block execution. Typically restricted; may require node flags.

> Public endpoints commonly reject this method as unsafe unless tracing is
> explicitly enabled on the node.

## Code Examples

## Request Parameters

- `block` (`string, required`): Hex-encoded block hash to replay.
- `targets` (`string, optional`): Comma-separated trace targets such as `state` or pallet-specific filters.
- `storageKeys` (`string, optional`): Optional storage keys to trace during block execution.
- `methods` (`string, optional`): Optional method filters used by tracing-enabled nodes.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_traceBlock",
  "params": [
    "<blockHash>",
    "state",
    "",
    ""
  ],
  "id": 1
}
```

## Response Fields

- `result` (`object, required`): Trace output emitted by tracing-enabled nodes. Public endpoints may reject this method as unsafe.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "blockTrace": [],
    "storageProofs": []
  }
}
```

## Error Responses

### Unsafe RPC Error

- Code: `4003`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 4003,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

---

## state_unsubscribeRuntimeVersion - Bittensor RPC Method

# state_unsubscribeRuntimeVersion - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `state_subscribeRuntimeVersion`. After calling this method, no further runtime version notifications will be delivered for that subscription. Provide the subscription ID that was returned when the subscription was created.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `state_subscribeRuntimeVersion`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_unsubscribeRuntimeVersion",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_unsubscribeRuntimeVersion",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Subscription cleanup** -- Cancel the runtime version subscription when your application no longer needs to track runtime upgrades.
- **Resource management** -- Free server-side resources in long-running WebSocket applications.
- **Subscription rotation** -- Stop an existing subscription before starting a new one after reconnection.

## Notes

- Always unsubscribe when done to prevent server-side resource leaks.
- The `chain_unsubscribeRuntimeVersion` method in the `chain` namespace serves the same purpose if the subscription was started with `chain_subscribeRuntimeVersion`.
- WebSocket-only; cannot be called over HTTP.

## Related Methods

- [`state_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_subscribeRuntimeVersion) -- Start the subscription this method cancels
- [`chain_subscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_subscribeRuntimeVersion) -- Subscribe via the chain namespace
- [`state_unsubscribeStorage`](https://www.dwellir.com/docs/bittensor/state_unsubscribeStorage) -- Cancel a storage subscription
- [`chain_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/chain_getRuntimeVersion) -- One-shot query for current runtime version

---

## state_unsubscribeStorage - Bittensor RPC Method

# state_unsubscribeStorage - Bittensor RPC Method

Cancels a WebSocket subscription that was started with `state_subscribeStorage`. After calling this method, no further storage change notifications will be delivered for that subscription. Provide the subscription ID that was returned when the subscription was created.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `state_subscribeStorage`.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_unsubscribeStorage",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if the subscription was successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "state_unsubscribeStorage",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Subscription cleanup** -- Cancel storage change notifications when your application no longer needs real-time updates for those keys.
- **Resource management** -- Free server-side memory and bandwidth by removing unused subscriptions.
- **Key set changes** -- Unsubscribe from the current key set and create a new subscription with a different set of keys to watch.

## Notes

- Always unsubscribe when done to prevent server-side resource leaks, especially in long-running applications.
- If the WebSocket connection drops, subscriptions are automatically cleaned up, but explicit unsubscription is still best practice.
- WebSocket-only; cannot be called over HTTP.

## Related Methods

- [`state_subscribeStorage`](https://www.dwellir.com/docs/bittensor/state_subscribeStorage) -- Start the subscription this method cancels
- [`state_unsubscribeRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_unsubscribeRuntimeVersion) -- Cancel a runtime version subscription
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bittensor/state_queryStorageAt) -- One-shot storage query as an alternative to subscriptions

---

## subnetInfo_getAllDynamicInfo - JSON-RPC Me...

# subnetInfo_getAllDynamicInfo - JSON-RPC Me...

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns dynamic info for all subnets (SCALE-encoded bytes).

## Code Examples

## Request Parameters

- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getAllDynamicInfo",
  "params": [
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded dynamic subnet information for every subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    5,
    2,
    1,
    0
  ]
}
```

---

## subnetInfo_getAllMechagraphs - JSON-RPC Me...

# subnetInfo_getAllMechagraphs - JSON-RPC Me...

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns mechagraphs for all subnets.

## Code Examples

## Request Parameters

- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getAllMechagraphs",
  "params": [
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded mechagraph payloads for all subnets.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    0,
    1,
    2,
    3
  ]
}
```

---

## subnetInfo_getAllMetagraphs - Bittensor RPC Method

# subnetInfo_getAllMetagraphs - Bittensor RPC Method

## Overview

The `subnetInfo_getAllMetagraphs` method returns the SCALE-encoded metagraphs for every active subnet on the Bittensor network in a single RPC call. This is the bulk equivalent of calling `subnetInfo_getMetagraph` for each netuid individually.

Each metagraph contains the complete topology of a subnet: all registered neurons, their stakes, trust scores, incentive values, weight matrices, emission distributions, and axon endpoints. By fetching all metagraphs at once, you can efficiently perform cross-subnet analysis and build network-wide monitoring dashboards.

> **Note:** This method returns a large payload since it includes metagraph data for every active subnet. Expect response sizes in the range of several megabytes for networks with many subnets. Consider using `subnetInfo_getMetagraph` for targeted single-subnet queries when you only need specific data.

## SCALE Decoding

The response decodes to `Vec<MetagraphInfo>`, where each `MetagraphInfo` contains the fields above. Due to the size of this response, decoding can be computationally intensive.

**Using `@polkadot/api`:** Register all Bittensor types including `MetagraphInfo` and `AxonInfo` before calling. The decoded result is an array that can be iterated. Each element has the same structure as the single `subnetInfo_getMetagraph` response.

**Using `bittensor` Python SDK:** There is no single SDK call that returns all metagraphs at once. Instead, iterate over subnet netuids: `for netuid in sub.get_all_subnet_netuids(): meta = sub.metagraph(netuid=netuid)`. For the raw RPC approach, decode `Vec<MetagraphInfo>` using the Bittensor type registry.

**Performance notes:**

- The total payload can exceed 10 MB for networks with 30+ active subnets
- Decoding time scales linearly with the number of subnets and neurons
- Consider processing the response in a background thread to avoid blocking your main application
- Cache the result and refresh at a cadence appropriate for your use case (once per tempo or less frequently)

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `at` (`DATA, optional`): Optional block hash used as the state reference for the query.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getAllMetagraphs",
    "params": [null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded metagraph payload for all subnets.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0123456789abcdef"
}
```

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getAllMetagraphs',
    'params': [None],
    'id': 1
}

response = requests.post(url, json=payload, timeout=60)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'All metagraphs SCALE data size: {len(scale_bytes)} bytes')
    print(f'Approximate size: {len(scale_bytes) // 2 / 1024:.1f} KB')
else:
    print('No metagraphs returned')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# for netuid in sub.get_all_subnet_netuids():
#     meta = sub.metagraph(netuid=netuid)
#     print(f"Subnet {netuid}: {meta.n} neurons, emissions: {meta.E.sum():.4f}")
```

### Full Python cross-subnet analysis

```python
import bittensor as bt
import numpy as np

sub = bt.subtensor(network='finney')
netuids = sub.get_all_subnet_netuids()

print(f"{'Netuid':>6} {'Neurons':>8} {'Validators':>11} {'Total Stake':>15} {'Emissions':>12} {'Avg Trust':>10}")
print("-" * 66)

total_neurons = 0
total_stake = 0

for netuid in netuids:
    try:
        meta = sub.metagraph(netuid=netuid)
        n_validators = sum(meta.validator_permit)
        total_s = meta.S.sum()
        total_e = meta.E.sum()
        avg_trust = meta.T.mean()

        total_neurons += meta.n
        total_stake += total_s

        print(f"{netuid:>6} {meta.n:>8} {n_validators:>11} {total_s:>14,.2f} {total_e:>11,.4f} {avg_trust:>9,.4f}")
    except Exception as e:
        print(f"{netuid:>6} Error: {e}")

print("-" * 66)
print(f"Total neurons: {total_neurons}, Total stake: {total_stake:,.2f} TAO")

# Find hotkeys registered on multiple subnets
hotkey_subnets = {}
for netuid in netuids:
    meta = sub.metagraph(netuid=netuid)
    for uid in range(meta.n):
        hk = meta.hotkeys[uid]
        hotkey_subnets.setdefault(hk, []).append(netuid)

multi_subnet = {k: v for k, v in hotkey_subnets.items() if len(v) > 1}
print(f"\nHotkeys on multiple subnets: {len(multi_subnet)}")
for hk, nets in sorted(multi_subnet.items(), key=lambda x: len(x[1]), reverse=True)[:5]:
    print(f"  {hk[:18]}.. on subnets: {nets}")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const allMetagraphs = await api.rpc.subnetInfo.getAllMetagraphs();
console.log('Raw data size:', allMetagraphs.toHex().length, 'bytes');

// With Bittensor type definitions registered, iterate decoded metagraphs
// for (const meta of allMetagraphs) {
//   console.log(`Subnet ${meta.netuid}: ${meta.n} neurons`);
// }

await api.disconnect();
```

## Common Use Cases

- **Network-wide analytics** — Build dashboards that aggregate neuron counts, total stake, emission distributions, and health metrics across all subnets.
- **Cross-subnet comparisons** — Compare validator performance, miner incentive scores, and consensus quality between subnets.
- **Global monitoring** — Track network growth by monitoring how many neurons are registered across all subnets over time.
- **Emission analysis** — Visualize how TAO emissions are distributed across subnets and identify which subnets receive the most rewards.
- **Research** — Study the global network topology and how different subnet configurations affect incentive dynamics.
- **Multi-subnet operators** — Monitor all your neurons across subnets in a single query instead of making individual calls.

## Related Methods

- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get metagraph for a single subnet (lighter response)
- [`subnetInfo_getSelectiveMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMetagraph) — Get filtered metagraph data for specific neuron UIDs
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get configuration info for all subnets
- [`subnetInfo_getAllDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllDynamicInfo) — Get dynamic info for all subnets
- [`subnetInfo_getAllMechagraphs`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllMechagraphs) — Get mechagraphs for all subnets

---

## subnetInfo_getColdkeyAutoStakeHotkey - Bittensor RPC Method

# subnetInfo_getColdkeyAutoStakeHotkey - Bittensor RPC Method

## Overview

The `subnetInfo_getColdkeyAutoStakeHotkey` method returns the hotkey that has been configured for auto-staking on behalf of a given coldkey within a Bittensor subnet. Auto-staking is a convenience feature that automatically delegates newly received TAO (such as emissions or transfers) to a designated hotkey, removing the need for manual re-staking.

When a coldkey sets an auto-stake hotkey, any TAO that arrives in the coldkey's account is automatically staked to the specified hotkey. This is particularly useful for validators and miners who want to compound their emissions without manual intervention.

## How Auto-Staking Works

Auto-staking in Bittensor provides automated compounding of rewards:

1. **Configuration:** The coldkey owner sets an auto-stake target hotkey using a Bittensor extrinsic (transaction)
2. **Trigger:** When the coldkey receives TAO (from emissions, transfers, or unstaking), the auto-stake mechanism detects the incoming funds
3. **Execution:** The received TAO is automatically staked to the configured hotkey
4. **Benefit:** This creates a compounding effect -- emissions earn more stake, which earns more emissions

Auto-staking can be subnet-specific, allowing different hotkeys for different subnets. This is useful for operators who run validators on multiple subnets with different hotkeys.

## SCALE Decoding

Decode the response as `Option<AccountId32>`.

- `[]` or `[0]` indicates `None` on shared endpoints that serialize SCALE bytes into JSON arrays.
- A populated response contains the SCALE bytes for `Some(AccountId32)`.
- Convert the decoded `AccountId32` back to SS58 with the chain's `ss58Format` when you need a human-readable hotkey.

## Code Examples

### Using SubstrateExamples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getColdkeyAutoStakeHotkey",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - Encoded hotkey bytes, where the method returns auto-staked hotkey info for a coldkey/netuid pair.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0a1b2c3d4e5f"
}
```

### Query with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getColdkeyAutoStakeHotkey',
    'params': ['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 1, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

scale_bytes = result.get('result', [])
if scale_bytes not in ([], [0]):
    print(f'SCALE bytes for auto-stake hotkey: {scale_bytes}')
else:
    print('No auto-stake hotkey configured')

# With bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# hotkey = sub.get_coldkey_auto_stake_hotkey(coldkey_ss58, netuid=1)
# print(f"Auto-stake target: {hotkey}")
```

### Full Python auto-stake verification

```python
import bittensor as bt

sub = bt.subtensor(network='finney')

# Check auto-stake configuration for a coldkey
coldkey_ss58 = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'

# Check across multiple subnets
netuids = sub.get_all_subnet_netuids()
print(f"Checking auto-stake for {coldkey_ss58[:18]}...\n")

auto_stake_found = False
for netuid in netuids:
    try:
        hotkey = sub.get_coldkey_auto_stake_hotkey(coldkey_ss58, netuid=netuid)
        if hotkey:
            auto_stake_found = True
            # Verify the hotkey is still a valid delegate
            delegate = sub.get_delegate_by_hotkey(hotkey)
            status = "active delegate" if delegate else "not a delegate"
            print(f"Subnet {netuid:>3}: auto-stake -> {hotkey[:18]}.. ({status})")
    except Exception as e:
        pass  # Method may not be supported for all subnets

if not auto_stake_found:
    print("No auto-stake configured on any subnet")

# Verify auto-stake is compounding correctly
# by checking stake changes over time
print("\n--- Compounding verification ---")
delegations = sub.get_delegated(coldkey_ss58)
for delegate_info, staked_amount in delegations:
    print(f"Delegate {delegate_info.hotkey_ss58[:18]}.. : "
          f"{staked_amount.tao:,.2f} TAO staked")
```

### Query with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const result = await api.rpc.subnetInfo.getColdkeyAutoStakeHotkey(
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  1,
  null,
);
if (!result.isEmpty) {
  console.log('Auto-stake hotkey:', result.toHuman());
} else {
  console.log('No auto-stake hotkey configured');
}

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getColdkeyAutoStakeHotkey",
    "params": ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1, null],
    "id": 1
  }'
```

## Common Use Cases

- **Staking automation** — Verify that auto-staking is correctly configured for a coldkey so emissions are automatically compounded.
- **Delegation management** — Check which hotkey is receiving auto-staked TAO for an account.
- **Validator operations** — Ensure your coldkey's auto-stake target points to the correct validator hotkey after key rotation or migration.
- **Account auditing** — Verify auto-stake configuration as part of security reviews or account management workflows.
- **Dashboard integration** — Display auto-stake configuration alongside other staking information in portfolio views.
- **Compounding verification** — Cross-reference auto-stake settings with actual delegation amounts to verify compounding is working as expected.
- **Multi-subnet operators** — Verify that subnet-specific auto-stake targets are correctly configured for each subnet you operate on.

## Related Methods

- [`delegateInfo_getDelegated`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegated) — Get all delegations for an account (verify auto-staked amounts)
- [`delegateInfo_getDelegate`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegate) — Get info for a specific delegate (verify target hotkey is valid)
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph for a subnet
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic subnet info (emission rates affect compounding)
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get info for all subnets

---

## subnetInfo_getDynamicInfo - Bittensor RPC Method

# subnetInfo_getDynamicInfo - Bittensor RPC Method

## Overview

The `subnetInfo_getDynamicInfo` method returns SCALE-encoded dynamic runtime information for a specific Bittensor subnet. While `subnetInfo_getSubnetsInfo` returns static configuration, this method returns real-time economic data that changes every tempo: emission rates, registration costs, alpha token pricing, and other values that reflect the current state of the subnet's economy.

This method is essential for understanding the economic dynamics of a subnet -- how much TAO it receives, what it costs to register, and how its internal token (alpha) is priced.

## SCALE Decoding

The response decodes to a single `SubnetDynamicInfo` struct using Bittensor's custom type registry.

**Using `@polkadot/api`:** Register the `SubnetDynamicInfo` type definition. Key types within the struct include `AccountId32`, `u16`, `u64`, `bool`, and `Option<SubnetIdentity>`. The `SubnetIdentity` struct typically contains `name` (`Vec<u8>`) and `description` (`Vec<u8>`) fields.

**Using `bittensor` Python SDK:** Use `sub.get_subnet_dynamic_info(netuid=N)` which returns a decoded object with all fields accessible as properties.

**Key decoding notes:**

- The `price` field represents the alpha-to-TAO exchange rate. The exact scaling factor depends on the runtime version
- `alpha_in` and `tao_in` define the AMM pool state. The constant product `k = alpha_in * tao_in` determines swap pricing
- `pending_emission` accumulates between tempos. After each tempo, pending emissions are distributed to neurons based on their scores
- `is_dynamic` indicates whether the subnet participates in the dynamic TAO system. Non-dynamic subnets have simpler emission mechanics
- `network_registered_at` is the genesis block for this subnet -- useful for calculating subnet age

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`INTEGER, required`): Numeric subnet identifier.
- `at` (`DATA, optional`): Optional block hash to query state at.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getDynamicInfo",
    "params": [1, null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded dynamic subnet info.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x9abcdeff"
}
```

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch dynamic info for subnet 1
payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getDynamicInfo',
    'params': [1, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Dynamic info SCALE data size: {len(scale_bytes)} bytes')
else:
    print('No dynamic info found for this subnet')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# dyn_info = sub.get_subnet_dynamic_info(netuid=1)
# print(f"Emission: {dyn_info.emission_value} RAO/block")
# print(f"Burn cost: {dyn_info.burn / 1e9:.4f} TAO")
# print(f"Alpha price: {dyn_info.price}")
```

### Full Python economic analysis

```python
import bittensor as bt

sub = bt.subtensor(network='finney')

# Analyze economics for a specific subnet
netuid = 1
dyn = sub.get_subnet_dynamic_info(netuid=netuid)
current_block = sub.block

print(f"=== Subnet {netuid} Economics ===")
print(f"Owner: {dyn.owner}")
print(f"Registered at block: {dyn.network_registered_at}")
print(f"Subnet age: {current_block - dyn.network_registered_at:,} blocks")
print(f"Tempo: {dyn.tempo} blocks (~{dyn.tempo * 12 / 60:.1f} minutes)")

# Emission analysis
daily_emission_tao = (dyn.emission_value / 1e9) * 7200
print(f"\nEmission: {dyn.emission_value / 1e9:.6f} TAO/block")
print(f"Daily emission: {daily_emission_tao:,.2f} TAO")
print(f"Pending emission: {dyn.pending_emission / 1e9:,.4f} TAO")
print(f"Pending alpha emission: {dyn.pending_alpha_emission / 1e9:,.4f} TAO")
print(f"Pending root emission: {dyn.pending_root_emission / 1e9:,.4f} TAO")

# Registration costs
print(f"\nRegistration burn cost: {dyn.burn / 1e9:,.4f} TAO")
print(f"Registration difficulty: {dyn.difficulty:,}")

# Dynamic TAO / Alpha token economics
if dyn.is_dynamic:
    print(f"\nDynamic TAO: Enabled")
    print(f"Alpha in pool: {dyn.alpha_in / 1e9:,.4f}")
    print(f"TAO in pool: {dyn.tao_in / 1e9:,.4f}")
    print(f"Alpha price: {dyn.price}")
    print(f"Pool constant k: {dyn.k}")
    print(f"Alpha distributed: {dyn.alpha_out / 1e9:,.4f}")
else:
    print(f"\nDynamic TAO: Disabled (traditional emission model)")

# Subnet identity
if dyn.subnet_identity:
    print(f"\nSubnet name: {dyn.subnet_identity.name}")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const dynamicInfo = await api.rpc.subnetInfo.getDynamicInfo(1);
console.log('Raw data:', dynamicInfo.toHex().slice(0, 80), '...');

// With Bittensor types registered:
// console.log('Emission:', dynamicInfo.emission_value.toString());
// console.log('Burn:', dynamicInfo.burn.toString());
// console.log('Dynamic TAO:', dynamicInfo.is_dynamic.toString());
// console.log('Alpha price:', dynamicInfo.price.toString());

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getDynamicInfo",
    "params": [1, null],
    "id": 1
  }'
```

zero | Handle both dynamic and non-dynamic subnet modes |
\| Invalid block hash | Returns JSON-RPC error | Verify block hash exists on chain |
\| Node not synced | May return stale economic data | Check `system_health` for sync status |
\| Rate limit exceeded | HTTP 429 | Cache results; refresh once per tempo at most |

## Common Use Cases

- **Subnet economics** — Analyze emission rates, alpha token pricing, and registration costs to understand the economic health of a subnet.
- **Emission tracking** — Monitor how emissions change over time for a subnet, including pending emissions not yet distributed.
- **Registration planning** — Check the current burn cost and difficulty before registering a neuron on a subnet.
- **Alpha token pricing** — Track the alpha-to-TAO exchange rate and AMM pool state for dynamic TAO subnets. Use `alpha_in` and `tao_in` to calculate slippage for planned swaps.
- **Investment analysis** — Evaluate subnet economics (emission share, token dynamics) for staking and delegation decisions.
- **Dashboard widgets** — Display real-time subnet economic indicators in monitoring tools.
- **Subnet age analysis** — Use `network_registered_at` to calculate subnet maturity and correlate with performance metrics.

## Related Methods

- [`subnetInfo_getAllDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllDynamicInfo) — Get dynamic info for all subnets
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get static configuration for all subnets
- [`subnetInfo_getLockCost`](https://www.dwellir.com/docs/bittensor/subnetInfo_getLockCost) — Get the TAO lock cost for subnet registration
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph topology for a subnet
- [`swap_currentAlphaPrice`](https://www.dwellir.com/docs/bittensor/swap_currentAlphaPrice) — Get the current alpha token price for swap operations

---

## subnetInfo_getLockCost - Bittensor RPC Method

# subnetInfo_getLockCost - Bittensor RPC Method

## Overview

The `subnetInfo_getLockCost` method returns the current TAO lock cost required to register a new subnet on the Bittensor network. Creating a subnet requires locking a significant amount of TAO, and this cost fluctuates dynamically based on network demand -- when more subnets are being created, the lock cost increases; when subnet registrations slow down, it decreases.

The lock cost acts as an economic barrier to prevent spam subnet creation and ensure that subnet operators have meaningful skin in the game. The locked TAO is returned when the subnet is dissolved, but is at risk if the subnet is pruned for underperformance.

This method returns a single numeric value (in RAO, where 1 TAO = 10^9 RAO) and is not SCALE-encoded like other subnet methods.

## How the Lock Cost Works

The lock cost is determined by a dynamic pricing mechanism:

- **Base cost:** There is a minimum lock cost floor that prevents subnets from being created for trivial amounts
- **Demand scaling:** As more subnets are registered, the cost increases exponentially. Each new subnet registration roughly doubles the cost for the next
- **Decay:** When no new subnets are registered, the cost gradually decays back toward the floor over time
- **Maximum subnets:** The network has a hard cap on the number of concurrent subnets. When this limit is reached, new subnets can only be created by replacing (pruning) the lowest-performing existing subnet
- **Lock vs. burn:** The locked TAO is not burned -- it remains locked to the subnet and is returned if the subnet is dissolved by its owner. However, if the subnet is pruned by the network for underperformance, the lock may be lost

This creates a competitive market for subnet slots: operators must evaluate whether the lock cost is justified by the expected emissions their subnet will earn.

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `at` (`DATA, optional`): Optional block hash used for the request.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getLockCost",
    "params": [null],
    "id": 1
  }'
```

## Response Fields

- `result` (`INTEGER, required`): Lock cost in the chain unit for subnet operations.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 100
}
```

### Query with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getLockCost',
    'params': [None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result') is not None:
    lock_cost_rao = int(result['result'])
    lock_cost_tao = lock_cost_rao / 1e9
    print(f'Current subnet lock cost: {lock_cost_tao:.4f} TAO')
    print(f'Lock cost in RAO: {lock_cost_rao:,}')
else:
    print('Could not fetch lock cost')

# With bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# lock_cost = sub.get_subnet_burn_cost()
# print(f"Lock cost: {lock_cost / 1e9:.4f} TAO")
```

### Full Python cost analysis

```python
import bittensor as bt
import requests
import json

sub = bt.subtensor(network='finney')

# Get current lock cost
lock_cost_rao = sub.get_subnet_burn_cost()
lock_cost_tao = lock_cost_rao / 1e9

# Get current network state for context
subnets = sub.get_all_subnets_info()
active_subnets = [s for s in subnets if s is not None]
total_subnets = len(active_subnets)

print(f"=== Subnet Registration Cost Analysis ===")
print(f"Current lock cost: {lock_cost_tao:,.2f} TAO ({lock_cost_rao:,} RAO)")
print(f"Active subnets: {total_subnets}")

# Estimate ROI: compare lock cost against potential emission income
# Average daily emission per subnet
total_daily_emissions = sum(
    (s.emission_value / 1e9) * 7200 for s in active_subnets
)
avg_daily_per_subnet = total_daily_emissions / total_subnets

print(f"\nTotal daily emissions (all subnets): {total_daily_emissions:,.2f} TAO")
print(f"Average daily emission per subnet: {avg_daily_per_subnet:,.2f} TAO")
print(f"Days to recover lock cost (at avg emission): {lock_cost_tao / avg_daily_per_subnet:,.0f}")
print(f"Annualized ROI (at avg emission): {(avg_daily_per_subnet * 365 / lock_cost_tao) * 100:,.1f}%")

# Track lock cost history by querying at past blocks
url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
current_block = sub.block
check_blocks = [current_block - 7200, current_block - 7200 * 7]  # 1 day ago, 1 week ago

for blocks_ago_block in check_blocks:
    try:
        block_hash = sub.get_block_hash(blocks_ago_block)
        payload = {
            'jsonrpc': '2.0',
            'method': 'subnetInfo_getLockCost',
            'params': [block_hash],
            'id': 1
        }
        resp = requests.post(url, json=payload)
        past_cost = int(resp.json()['result']) / 1e9
        change = ((lock_cost_tao - past_cost) / past_cost) * 100
        print(f"Lock cost at block {blocks_ago_block}: {past_cost:,.2f} TAO ({change:+.1f}% change)")
    except Exception as e:
        print(f"Could not fetch historical cost: {e}")
```

### Query with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const lockCost = await api.rpc.subnetInfo.getLockCost();
const tao = Number(lockCost) / 1e9;
console.log(`Current subnet lock cost: ${tao.toFixed(4)} TAO`);

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getLockCost",
    "params": [null],
    "id": 1
  }'
```

zero or negative | Should not happen; indicates node issue | Retry with a different node or check node health |
\| Rate limit exceeded | HTTP 429 | Cache results; lock cost changes slowly (poll every few minutes) |

## Common Use Cases

- **Subnet creation planning** — Check the current lock cost before committing to register a new subnet. Time your registration when costs are lower.
- **Cost monitoring** — Track how the lock cost changes over time to understand network demand for new subnets.
- **Budget estimation** — Calculate the total TAO required for subnet operations (lock cost + registration burns for neurons + staking).
- **Economic analysis** — Study the relationship between lock cost dynamics and subnet creation/dissolution rates.
- **Alerting** — Set up alerts when the lock cost drops below a threshold, signaling a good time to register.
- **ROI modeling** — Compare the lock cost against expected subnet emissions to evaluate the investment case for creating a new subnet.
- **Competitive intelligence** — Monitor lock cost trends to anticipate when new subnets are likely to be created.

## Related Methods

- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic info including emission rates and registration costs for a subnet
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get configuration info for all subnets
- [`subnetInfo_getSubnetToPrune`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetToPrune) — Check which subnet is next in line for pruning
- [`subnetInfo_getSubnetInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetInfo) — Get info for a single subnet
- [`swap_currentAlphaPrice`](https://www.dwellir.com/docs/bittensor/swap_currentAlphaPrice) — Get the current alpha token price

---

## subnetInfo_getMechagraph - Bittensor RPC Method

# subnetInfo_getMechagraph - Bittensor RPC Method

## Overview

The `subnetInfo_getMechagraph` method returns the SCALE-encoded mechagraph for a specific subnet (`netuid`) and mechanism ID (`mecid`) on the Bittensor network. The mechagraph is a more granular view of subnet topology that captures mechanism-specific data -- how neurons perform within a particular evaluation mechanism running on the subnet.

While the metagraph provides the overall subnet state, the mechagraph drills down into the individual mechanisms that a subnet uses to evaluate miners. A subnet can run multiple mechanisms (each identified by a `mecid`), and the mechagraph captures the incentive and consensus data specific to that mechanism.

This method is used by advanced analytics tools, subnet operators fine-tuning their evaluation mechanisms, and researchers studying how different mechanisms affect incentive distribution.

## Metagraph vs. Mechagraph

Understanding the distinction between metagraph and mechagraph is important:

- **Metagraph:** Aggregated subnet-level view. Contains the combined scores, emissions, and weights across all mechanisms. This is what most users interact with.
- **Mechagraph:** Mechanism-level view. Contains scores and weights specific to a single evaluation mechanism. Subnets that run multiple evaluation criteria (e.g., latency + accuracy) expose separate mechagraphs for each.

For subnets with a single mechanism (mecid=0), the mechagraph is essentially identical to the metagraph. Multi-mechanism subnets reveal how each individual mechanism contributes to the final aggregated scores.

## SCALE Decoding

The response decodes to a `MechagraphInfo` struct using Bittensor's custom type registry.

**Using `@polkadot/api`:** Register Bittensor types including `MechagraphInfo`. The struct layout is similar to `MetagraphInfo` but includes the additional `mecid` field and may have fewer fields (no axon info, no coldkeys, depending on runtime version).

**Using `bittensor` Python SDK:** Use `sub.get_mechagraph(netuid=N, mecid=M)` if available in your SDK version. For older SDK versions, use the raw JSON-RPC call and decode with the `scalecodec` library.

**Key decoding notes:**

- Score fields (trust, consensus, incentive, dividends) are u16 scaled to 0--65535. Divide by 65535 for float values
- Mechanism-specific scores may differ from the aggregated metagraph scores. A miner might score high on mechanism 0 but low on mechanism 1
- The `weights` matrix in a mechagraph reflects how validators evaluate miners specifically for this mechanism
- Emissions in the mechagraph represent the portion of total subnet emissions allocated to this mechanism

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `mecid` (`INTEGER, required`): Mechagraph ID within the subnet.
- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getMechagraph",
    "params": [1, 0, null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded mechagraph for the requested subnet/mechid.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x001122334455"
}
```

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch mechagraph for subnet 1, mechanism 0
payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getMechagraph',
    'params': [1, 0, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Mechagraph SCALE data size: {len(scale_bytes)} bytes')
else:
    print('No mechagraph found for this subnet/mechanism')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# mechagraph = sub.get_mechagraph(netuid=1, mecid=0)
# print(f"Mechanism 0: {mechagraph.n} neurons")
```

### Full Python mechanism comparison

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'
netuid = 1

# Fetch mechagraphs for multiple mechanisms and compare
for mecid in range(3):  # Try mechanisms 0, 1, 2
    payload = {
        'jsonrpc': '2.0',
        'method': 'subnetInfo_getMechagraph',
        'params': [netuid, mecid, None],
        'id': mecid + 1
    }
    response = requests.post(url, json=payload)
    result = response.json()

    if result.get('result'):
        data_size = len(result['result']) // 2
        print(f"Mechanism {mecid}: {data_size:,} bytes of data")
    else:
        print(f"Mechanism {mecid}: not found (subnet may have fewer mechanisms)")
        break

# With bittensor SDK for deeper analysis:
# import bittensor as bt
# import numpy as np
# sub = bt.subtensor(network='finney')
#
# mech0 = sub.get_mechagraph(netuid=1, mecid=0)
# mech1 = sub.get_mechagraph(netuid=1, mecid=1)
#
# # Compare incentive scores across mechanisms
# for uid in range(min(mech0.n, mech1.n, 10)):
#     print(f"UID {uid}: mech0 incentive={mech0.I[uid]:.4f}, "
#           f"mech1 incentive={mech1.I[uid]:.4f}")
#
# # Find neurons that perform well on one mechanism but poorly on another
# divergent = np.abs(mech0.I - mech1.I) > 0.1
# print(f"Neurons with divergent mechanism scores: {divergent.sum()}")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Fetch mechagraph for subnet 1, mechanism 0
const mechagraph = await api.rpc.subnetInfo.getMechagraph(1, 0);
console.log('Raw data size:', mechagraph.toHex().length, 'bytes');

// With Bittensor type definitions registered:
// console.log('Mechanism:', mechagraph.mecid.toNumber());
// console.log('Neurons:', mechagraph.n.toNumber());

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getMechagraph",
    "params": [1, 0, null],
    "id": 1
  }'
```

## Common Use Cases

- **Advanced subnet analytics** — Analyze how individual mechanisms within a subnet distribute incentives and evaluate miners differently.
- **Mechanism tuning** — Subnet operators use mechagraph data to understand how their evaluation mechanisms are performing and adjust parameters.
- **Performance monitoring** — Track mechanism-specific trust and incentive scores for individual neurons to identify strengths across different tasks.
- **Research** — Study how multi-mechanism subnets allocate rewards and whether different mechanisms produce different consensus outcomes.
- **Validator tooling** — Build tools that help validators understand how their weights and evaluations affect mechanism-level outcomes.
- **Miner optimization** — Miners can use mechagraph data to identify which mechanisms they perform well on and optimize accordingly.

## Related Methods

- [`subnetInfo_getAllMechagraphs`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllMechagraphs) — Get mechagraphs for all subnets
- [`subnetInfo_getSelectiveMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMechagraph) — Get filtered mechagraph data for specific neuron UIDs
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the overall metagraph for a subnet (aggregated view)
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic economic info for a subnet
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed info for a specific neuron

---

## subnetInfo_getMetagraph - Bittensor RPC Method

# subnetInfo_getMetagraph - Bittensor RPC Method

## Overview

The `subnetInfo_getMetagraph` method returns the SCALE-encoded metagraph for a specific Bittensor subnet identified by its `netuid`. The metagraph is the most important data structure in the Bittensor network -- it represents the complete topology of a subnet, including every registered neuron (validator or miner), their stakes, trust scores, incentive values, emission distributions, and weight matrices.

The metagraph is the foundation for:

- Understanding how a subnet's consensus is operating
- Monitoring validator and miner performance
- Analyzing emission flows and incentive distributions
- Building subnet explorer dashboards and analytics tools

Each subnet in Bittensor has its own metagraph. The root network (netuid 0) coordinates emissions across all subnets, while application subnets (netuid 1+) each run specialized AI/ML tasks.

## SCALE Decoding

The metagraph is one of the largest SCALE-encoded structures in Bittensor. Understanding the decoding process is important for working with it correctly.

**Using `@polkadot/api`:** Register the Bittensor type definitions including `MetagraphInfo`, `AxonInfo`, and related types. The `AxonInfo` struct typically contains: `version` (u32), `ip` (u128), `port` (u16), `ip_type` (u8), `protocol` (u8), `placeholder1` (u8), `placeholder2` (u8). IP addresses are stored as u128 and need conversion -- for IPv4, the value fits in 4 bytes.

**Using `bittensor` Python SDK:** The recommended approach is `bt.subtensor(network='finney').metagraph(netuid=N)`, which returns a `Metagraph` object with NumPy arrays for all vector fields (e.g., `meta.S` for stake, `meta.I` for incentive, `meta.E` for emission).

**Score normalization:** All u16 score fields (trust, consensus, incentive, dividends, rank, validator\_trust) are scaled to the range 0--65535. Divide by 65535 to get float values between 0.0 and 1.0. The bittensor SDK does this conversion automatically.

**Weight matrix format:** Each entry in `weights` is a sparse vector of `(target_uid, weight)` pairs. The weight values are u16 (0--65535). This represents how validators evaluate miners -- a higher weight means the validator considers that miner to be performing better.

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getMetagraph",
    "params": [1, null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded metagraph for the specified subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xabcdef"
}
```

### Decode with Python (bittensor SDK)

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch metagraph for subnet 1
payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getMetagraph',
    'params': [1, None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Metagraph SCALE data size: {len(scale_bytes)} bytes')
else:
    print('No metagraph found for this subnet')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# meta = sub.metagraph(netuid=1)
# print(f"Subnet 1: {meta.n} neurons")
# print(f"Top validator stake: {meta.S[meta.S.argmax()]:.2f} TAO")
# print(f"Total emissions: {meta.E.sum():.4f} TAO/tempo")
```

### Full Python metagraph analysis

```python
import bittensor as bt
import numpy as np

sub = bt.subtensor(network='finney')
meta = sub.metagraph(netuid=1)

print(f"=== Subnet 1 Metagraph (block {meta.block}) ===")
print(f"Neurons: {meta.n}")
print(f"Total stake: {meta.S.sum():,.2f} TAO")
print(f"Total emissions: {meta.E.sum():,.6f} TAO/tempo")

# Identify validators (those with validator permits)
validator_mask = np.array(meta.validator_permit)
validator_uids = np.where(validator_mask)[0]
miner_uids = np.where(~validator_mask)[0]
print(f"Validators: {len(validator_uids)}, Miners: {len(miner_uids)}")

# Top 5 validators by stake
top_validators = np.argsort(meta.S[validator_mask])[::-1][:5]
print("\nTop 5 validators by stake:")
for idx in top_validators:
    uid = validator_uids[idx]
    print(f"  UID {uid}: stake={meta.S[uid]:,.2f} TAO, "
          f"vtrust={meta.Tv[uid]:.4f}, dividends={meta.D[uid]:.4f}")

# Top 5 miners by incentive
top_miners = np.argsort(meta.I[~validator_mask])[::-1][:5]
print("\nTop 5 miners by incentive:")
for idx in top_miners:
    uid = miner_uids[idx]
    print(f"  UID {uid}: incentive={meta.I[uid]:.4f}, "
          f"trust={meta.T[uid]:.4f}, emission={meta.E[uid]:.6f} TAO")

# Weight analysis: how concentrated are validator weights?
for uid in validator_uids[:3]:
    weights = meta.W[uid]
    non_zero = weights[weights > 0]
    if len(non_zero) > 0:
        print(f"\nValidator UID {uid}: sets weights on {len(non_zero)} miners")
        print(f"  Max weight: {non_zero.max():.4f}, Mean: {non_zero.mean():.4f}")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Fetch metagraph for subnet 1
const metagraph = await api.rpc.subnetInfo.getMetagraph(1);
console.log('Raw metagraph size:', metagraph.toHex().length, 'bytes');

// With proper Bittensor type definitions registered,
// the result auto-decodes to structured fields:
// console.log('Neurons:', metagraph.n.toNumber());
// console.log('Hotkeys:', metagraph.hotkeys.map(h => h.toHuman()));

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getMetagraph",
    "params": [1, null],
    "id": 1
  }'
```

ze. Cache metagraph data and refresh once per tempo for most use cases.

## Common Use Cases

- **Subnet analytics** — Build dashboards showing neuron counts, emission distributions, stake concentrations, and consensus health for each subnet.
- **Validator monitoring** — Track validator trust, consensus scores, and weight-setting patterns to detect anomalies or poor performance.
- **Miner monitoring** — Monitor miner incentive scores, rank changes, and emission earnings over time.
- **Weight analysis** — Analyze the weight matrix to understand how validators evaluate miners and detect potential gaming or collusion.
- **Staking decisions** — Use metagraph data to identify high-performing validators before delegating TAO.
- **Research** — Study incentive dynamics, consensus convergence, and network topology evolution across subnets.
- **Axon discovery** — Find miner/validator endpoints for direct network communication.

## Related Methods

- [`subnetInfo_getAllMetagraphs`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllMetagraphs) — Get metagraphs for all subnets in one call
- [`subnetInfo_getSelectiveMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMetagraph) — Get metagraph data filtered by specific neuron UIDs
- [`subnetInfo_getMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMechagraph) — Get mechagraph for a subnet (mechanism-level view)
- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) — Get subnet configuration info
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic subnet info (emissions, registration costs)
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed info for a specific neuron by UID

---

## subnetInfo_getSelectiveMechagraph - Bittensor RPC Method

# subnetInfo_getSelectiveMechagraph - Bittensor RPC Method

## Overview

The `subnetInfo_getSelectiveMechagraph` method returns SCALE-encoded mechagraph data filtered to include only the specified neuron UIDs. This is the targeted version of `subnetInfo_getMechagraph` -- instead of returning the complete mechagraph for all neurons in a mechanism, it returns data only for the neurons you are interested in.

This method is useful when monitoring a specific set of miners or validators within a mechanism and you want to avoid downloading and parsing the full mechagraph payload. For subnets with hundreds or thousands of neurons, selective queries can significantly reduce bandwidth and processing time.

## Selective vs. Full Mechagraph

Choose the right method based on your use case:

| Scenario                                | Recommended Method                            |
| --------------------------------------- | --------------------------------------------- |
| Monitor 1--50 specific neurons          | `getSelectiveMechagraph` (this method)        |
| Analyze full mechanism topology         | [`getMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMechagraph) |
| Compare mechanisms for specific neurons | This method, called once per mecid            |
| Build complete mechanism heatmaps       | [`getMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMechagraph) |
| Real-time alerting on key neurons       | This method (lower bandwidth per poll)        |

## SCALE Decoding

The response decodes to a filtered `MechagraphInfo` struct. The structure is the same as the full mechagraph but all vector fields only contain entries for the requested UIDs, in the order they were requested.

**Using `@polkadot/api`:** Register Bittensor types including `MechagraphInfo`. The decoded result has identical field types to the full mechagraph response.

**Using `bittensor` Python SDK:** Some SDK versions support `sub.get_selective_mechagraph(netuid, mecid, uids)`. For manual decoding, use `scalecodec` with the Bittensor type registry.

**Key decoding notes:**

- The response vectors are indexed by position, not by UID. The first element corresponds to the first UID in your request, and so on
- If a requested UID does not exist in the mechanism, it may be omitted from the response or returned with zero values (depending on runtime version)
- The `weights` and `bonds` matrices for selected neurons still reference UIDs from the full subnet -- they are not re-indexed
- Score fields are u16 scaled to 0--65535; divide by 65535 for float values

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `start` (`INTEGER, required`): Start index for filtered mechagraph extraction.
- `mechids` (`Array, required`): List of mechagraph indexes to return.
- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSelectiveMechagraph",
    "params": [1, 0, [0, 1], null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded subset of mechagraph entries.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xdeadbeef"
}
```

### Query with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch mechagraph data for neurons 0 and 1 in subnet 1, mechanism 0
payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getSelectiveMechagraph',
    'params': [1, 0, [0, 1], None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Selective mechagraph data size: {len(scale_bytes)} bytes')
else:
    print('No data returned for specified neurons')

# Query a larger set of UIDs for batch monitoring:
# uids_to_watch = [0, 5, 12, 42, 100]
# payload['params'] = [1, 0, uids_to_watch, None]
# response = requests.post(url, json=payload)
```

### Full Python monitoring example

```python
import requests
import json
import time

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Monitor specific neurons across mechanisms
netuid = 1
watched_uids = [0, 3, 7, 15, 42]

def fetch_selective_mechagraph(netuid, mecid, uids):
    payload = {
        'jsonrpc': '2.0',
        'method': 'subnetInfo_getSelectiveMechagraph',
        'params': [netuid, mecid, uids, None],
        'id': 1
    }
    response = requests.post(url, json=payload)
    return response.json()

# Compare performance across mechanisms for watched neurons
for mecid in range(2):
    result = fetch_selective_mechagraph(netuid, mecid, watched_uids)
    if result.get('result'):
        data = result['result']
        print(f"Mechanism {mecid}: {len(data) // 2:,} bytes for {len(watched_uids)} neurons")
    else:
        print(f"Mechanism {mecid}: not available")

# With bittensor SDK for decoded analysis:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
#
# # Poll mechanism scores for watched neurons
# for mecid in [0, 1]:
#     mech = sub.get_selective_mechagraph(netuid=1, mecid=mecid, uids=watched_uids)
#     if mech:
#         for i, uid in enumerate(watched_uids):
#             print(f"  UID {uid}: incentive={mech.I[i]:.4f}, trust={mech.T[i]:.4f}")
```

### Query with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Fetch selective mechagraph for UIDs 0 and 1
const result = await api.rpc.subnetInfo.getSelectiveMechagraph(1, 0, [0, 1]);
console.log('Selective data size:', result.toHex().length, 'bytes');

// Monitor a watchlist
const watchlist = [0, 5, 12, 42, 100];
const selective = await api.rpc.subnetInfo.getSelectiveMechagraph(1, 0, watchlist);
console.log(`Data for ${watchlist.length} neurons:`, selective.toHex().length, 'bytes');

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSelectiveMechagraph",
    "params": [1, 0, [0, 1, 5, 10], null],
    "id": 1
  }'
```

zeroed | Verify UIDs exist in the subnet first |
\| Too many UIDs | No hard limit, but large requests approach full mechagraph cost | For 50+ UIDs, consider fetching the full mechagraph instead |
\| Invalid block hash | Returns JSON-RPC error | Verify block hash exists |

## Common Use Cases

- **Targeted monitoring** — Track specific miners or validators within a mechanism without downloading the entire mechagraph.
- **Performance dashboards** — Build monitoring views that focus on a user's own neurons or a watched set of neurons.
- **Bandwidth optimization** — Reduce payload size when you only need data for a small subset of the subnet's neurons.
- **Alerting systems** — Poll specific neurons' incentive and trust scores at regular intervals with minimal overhead.
- **Competitive analysis** — Monitor specific competitors' mechanism-level performance without full data retrieval.
- **Cross-mechanism comparison** — Compare how the same neurons score across different mechanisms by calling this method once per mecid.

## Related Methods

- [`subnetInfo_getMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMechagraph) — Get the full mechagraph for a subnet mechanism
- [`subnetInfo_getAllMechagraphs`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllMechagraphs) — Get mechagraphs for all subnets
- [`subnetInfo_getSelectiveMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMetagraph) — Get filtered metagraph data for specific neuron UIDs
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the full metagraph for a subnet
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed info for a single neuron by UID

---

## subnetInfo_getSelectiveMetagraph - Bittensor RPC Method

# subnetInfo_getSelectiveMetagraph - Bittensor RPC Method

## Overview

The `subnetInfo_getSelectiveMetagraph` method returns SCALE-encoded metagraph data filtered to include only the specified neuron UIDs. This is the targeted version of `subnetInfo_getMetagraph` -- instead of returning the complete metagraph for all neurons in a subnet, it returns data only for the neurons you specify.

This method is ideal for monitoring applications that track a specific set of validators or miners. For large subnets with thousands of neurons, fetching the full metagraph on every poll is expensive in both bandwidth and processing time. The selective metagraph lets you efficiently query just the neurons you care about.

## Selective vs. Full Metagraph

Choose the right method based on your needs:

| Scenario                             | Recommended Method                          |
| ------------------------------------ | ------------------------------------------- |
| Monitor your own 1--20 neurons       | `getSelectiveMetagraph` (this method)       |
| Build a full subnet explorer         | [`getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) |
| Alert on specific neuron performance | This method (poll every tempo)              |
| Analyze entire weight matrix         | [`getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) |
| Mobile app with bandwidth limits     | This method                                 |
| Cross-subnet neuron tracking         | This method, called per subnet              |

**Bandwidth savings example:** A subnet with 256 neurons might return a 500 KB metagraph. Querying 5 specific neurons returns roughly 10--20 KB -- a 25--50x reduction.

## SCALE Decoding

The response decodes to a filtered `MetagraphInfo` struct. Vector fields are ordered by the position of UIDs in your request (not by UID value).

**Using `@polkadot/api`:** Register Bittensor types including `MetagraphInfo` and `AxonInfo`. The decoded result has identical field types to the full metagraph.

**Using `bittensor` Python SDK:** Some SDK versions support selective metagraph queries directly. For manual decoding, parse the hex as a `MetagraphInfo` struct using the Bittensor type registry.

**Key decoding notes:**

- Response vectors are positionally indexed: element 0 corresponds to the first UID you requested, element 1 to the second, and so on
- The `weights` and `bonds` sparse vectors for each neuron still reference global UIDs from the full subnet, not re-indexed positions
- If a requested UID is not registered (empty slot), it may be omitted or have `is_null: true`
- `AxonInfo` contains `ip` (u128), `port` (u16), `version` (u32), `ip_type` (u8), `protocol` (u8). IPv4 addresses fit in 4 bytes of the u128
- Score fields are u16 (0--65535). Divide by 65535 for normalized float values (0.0--1.0)

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `metagraphIndexes` (`Array, required`): List of metagraph indexes to return.
- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSelectiveMetagraph",
    "params": [1, [0, 1], null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded subset of metagraph entries.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xfeedbeef"
}
```

### Query with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Fetch metagraph data for neurons 0 and 1 in subnet 1
payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getSelectiveMetagraph',
    'params': [1, [0, 1], None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'Selective metagraph data size: {len(scale_bytes)} bytes')
else:
    print('No data returned for specified neurons')

# Monitor a watch list of neurons:
# my_neurons = [3, 17, 42, 88, 155]
# payload['params'] = [1, my_neurons, None]
# response = requests.post(url, json=payload)
```

### Full Python monitoring dashboard

```python
import requests
import json
import time

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Define neurons to monitor across subnets
watchlist = {
    1: [0, 3, 15, 42],      # Subnet 1: specific UIDs
    18: [0, 1, 7, 22],      # Subnet 18: specific UIDs
}

def fetch_selective(netuid, uids):
    payload = {
        'jsonrpc': '2.0',
        'method': 'subnetInfo_getSelectiveMetagraph',
        'params': [netuid, uids, None],
        'id': 1
    }
    resp = requests.post(url, json=payload, timeout=15)
    return resp.json()

# Periodic monitoring loop
for netuid, uids in watchlist.items():
    result = fetch_selective(netuid, uids)
    if result.get('result'):
        data_size = len(result['result']) // 2
        print(f"Subnet {netuid}: fetched {data_size:,} bytes for {len(uids)} neurons")
    else:
        print(f"Subnet {netuid}: no data")

# With bittensor SDK for decoded data:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
#
# for netuid, uids in watchlist.items():
#     meta = sub.metagraph(netuid=netuid)
#     for uid in uids:
#         if uid < meta.n:
#             print(f"Subnet {netuid} UID {uid}: "
#                   f"incentive={meta.I[uid]:.4f}, "
#                   f"emission={meta.E[uid]:.6f} TAO, "
#                   f"active={meta.active[uid]}, "
#                   f"last_update=block {meta.last_update[uid]}")
```

### Query with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Fetch selective metagraph for UIDs 0 and 1 on subnet 1
const result = await api.rpc.subnetInfo.getSelectiveMetagraph(1, [0, 1]);
console.log('Selective data size:', result.toHex().length, 'bytes');

// Monitor a larger watchlist
const watchlist = [0, 3, 7, 15, 42, 88, 155];
const selective = await api.rpc.subnetInfo.getSelectiveMetagraph(1, watchlist);
console.log(`Data for ${watchlist.length} neurons:`, selective.toHex().length, 'bytes');

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSelectiveMetagraph",
    "params": [1, [0, 1, 5, 10], null],
    "id": 1
  }'
```

zeroed | Get `n` from `getMetagraph` or `getSubnetsInfo` to know valid UID range |
\| Too many UIDs | No hard limit, but approaches full metagraph cost | For large sets (50+), fetch the full metagraph instead |
\| Invalid block hash | Returns JSON-RPC error | Verify block hash exists |
\| Node not synced | May return stale data | Check `system_health` |

## Common Use Cases

- **Targeted monitoring** — Track specific validators or miners within a subnet without the overhead of the full metagraph.
- **Personal dashboards** — Build monitoring views focused on a user's own registered neurons across subnets.
- **Bandwidth optimization** — Significantly reduce response sizes for large subnets when polling frequently.
- **Alerting systems** — Efficiently poll specific neurons' trust, incentive, and emission scores at regular intervals.
- **Competitive intelligence** — Monitor specific competitors' performance metrics without downloading all subnet data.
- **Mobile apps** — Keep payload sizes small for mobile-friendly subnet monitoring applications.
- **Multi-subnet tracking** — Monitor the same operator's neurons across multiple subnets by making one call per subnet.

## Related Methods

- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the full metagraph for a subnet
- [`subnetInfo_getAllMetagraphs`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllMetagraphs) — Get metagraphs for all subnets
- [`subnetInfo_getSelectiveMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSelectiveMechagraph) — Get filtered mechagraph data for specific neuron UIDs
- [`subnetInfo_getMechagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMechagraph) — Get the full mechagraph for a subnet mechanism
- [`neuronInfo_getNeuron`](https://www.dwellir.com/docs/bittensor/neuronInfo_getNeuron) — Get detailed info for a single neuron by UID

---

## subnetInfo_getSubnetHyperparams - JSON-RPC...

# subnetInfo_getSubnetHyperparams - JSON-RPC...

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns SCALE-encoded hyperparameters for the specified subnet id.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getSubnetHyperparams",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded subnet hyperparameter payload.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    40,
    254,
    255
  ]
}
```

---

## subnetInfo_getSubnetHyperparamsV2 - JSON-R...

# subnetInfo_getSubnetHyperparamsV2 - JSON-R...

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns updated hyperparameters for a subnet (SCALE bytes).

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getSubnetHyperparamsV2",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded v2 hyperparameter payload for the subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    40,
    254,
    255
  ]
}
```

---

## subnetInfo_getSubnetInfo - JSON-RPC Method

# subnetInfo_getSubnetInfo - JSON-RPC Method

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns SCALE-encoded info for the subnet identified by `netuid`.

## Code Examples

## Request Parameters

- `netuid` (`INTEGER, required`): Subnet identifier.
- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSubnetInfo",
    "params": [1, null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded subnet metadata and dynamic information.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1234beef"
}
```

---

## subnetInfo_getSubnetInfo_v2 - JSON-RPC Method

# subnetInfo_getSubnetInfo_v2 - JSON-RPC Method

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns V2 subnet info for `netuid`.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getSubnetInfo_v2",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded subnet metadata and summary fields for the requested subnet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    4,
    40,
    254
  ]
}
```

---

## subnetInfo_getSubnetsInfo - Bittensor RPC Method

# subnetInfo_getSubnetsInfo - Bittensor RPC Method

## Overview

The `subnetInfo_getSubnetsInfo` method returns SCALE-encoded configuration and metadata for all active subnets on the Bittensor network. Unlike the metagraph methods which return per-neuron topology data, this method returns the subnet-level configuration: owner, emission values, hyperparameters, and registration settings.

This is the primary method for subnet discovery -- building a list of all active subnets with their key properties. Use it to populate subnet selection UIs, build network overview dashboards, or analyze how different subnets are configured.

## SCALE Decoding

The response decodes to `Vec<Option<SubnetInfo>>` where each element corresponds to a netuid slot. Empty slots (dissolved subnets) are represented as `None`.

**Using `@polkadot/api`:** Register the `SubnetInfo` type definition from the Bittensor type registry. The decoded result is an array of optional subnet info structs. Iterate and skip `None` entries for dissolved subnets.

**Using `bittensor` Python SDK:** Use `sub.get_all_subnets_info()` which returns a list of `SubnetInfo` objects with all fields accessible as properties.

**Key decoding notes:**

- `rho` and `kappa` are consensus hyperparameters. Higher `kappa` (closer to 65535) means sharper weight consensus; lower values produce smoother distributions
- `tempo` is in blocks. At \~12 seconds per block, a tempo of 360 blocks means a consensus epoch roughly every 72 minutes
- `difficulty` is relevant only for Proof-of-Work registration. Many subnets use burn-based registration instead (check `burn` field)
- `max_allowed_uids` defines the maximum subnet capacity. When `subnetwork_n` reaches this limit, new registrations must replace existing neurons
- For live pricing, pool state, and subnet economics, use `subnetInfo_getDynamicInfo` rather than `subnetInfo_getSubnetsInfo`

## Code Examples

### Using SubstrateExamples

## Request Parameters

- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSubnetsInfo",
    "params": [null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded data for all subnets.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xaa55cc77"
}
```

### Decode with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getSubnetsInfo',
    'params': [None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result'):
    scale_bytes = result['result']
    print(f'All subnets info size: {len(scale_bytes)} bytes')
else:
    print('No subnet info returned')

# To decode with bittensor SDK:
# import bittensor as bt
# sub = bt.subtensor(network='finney')
# subnets = sub.get_all_subnets_info()
# for s in subnets:
#     print(f"Subnet {s.netuid}: neurons={s.subnetwork_n}/{s.max_n}, "
#           f"tempo={s.tempo}, emission={s.emission_value}")
```

### Full Python subnet comparison

```python
import bittensor as bt

sub = bt.subtensor(network='finney')
subnets = sub.get_all_subnets_info()

print(f"{'Netuid':>6} {'Neurons':>10} {'Max':>6} {'Tempo':>6} {'Burn (TAO)':>12} "
      f"{'Emission/day':>14} {'Immunity':>9} {'Owner':<18}")
print("-" * 95)

total_emission = 0
for s in subnets:
    if s is None:
        continue
    burn_tao = s.burn / 1e9
    daily_emission_tao = (s.emission_value / 1e9) * 7200  # ~7200 blocks/day
    total_emission += daily_emission_tao

    print(f"{s.netuid:>6} {s.subnetwork_n:>6}/{s.max_n:<4} {s.tempo:>6} "
          f"{burn_tao:>11,.2f} {daily_emission_tao:>13,.2f} {s.immunity_period:>9} "
          f"{str(s.owner)[:16]}..")

print(f"\nTotal subnets: {len([s for s in subnets if s is not None])}")
print(f"Total daily emissions: {total_emission:,.2f} TAO")

# Find subnets with available capacity
open_subnets = [s for s in subnets if s and s.subnetwork_n < s.max_n]
print(f"Subnets with open slots: {len(open_subnets)}")
for s in sorted(open_subnets, key=lambda x: x.burn):
    available = s.max_n - s.subnetwork_n
    print(f"  Subnet {s.netuid}: {available} slots available, burn={s.burn/1e9:.2f} TAO")
```

### Decode with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const subnetsInfo = await api.rpc.subnetInfo.getSubnetsInfo();
console.log('Raw data size:', subnetsInfo.toHex().length, 'bytes');

// With Bittensor types registered, iterate subnet configs:
// for (const subnet of subnetsInfo) {
//   if (!subnet.isNone) {
//     const s = subnet.unwrap();
//     console.log(`Subnet ${s.netuid}: ${s.subnetwork_n}/${s.max_n} neurons`);
//   }
// }

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSubnetsInfo",
    "params": [null],
    "id": 1
  }'
```

## Common Use Cases

- **Subnet discovery** — Build a catalog of all active subnets with their key parameters for users to browse and compare.
- **Parameter analysis** — Compare hyperparameters (tempo, difficulty, immunity period) across subnets to understand how they are configured.
- **Network overview** — Display total neurons, emission allocations, and growth trends across all subnets.
- **Registration planning** — Check registration difficulty and burn costs before deciding which subnet to register on. Find subnets with available capacity.
- **Governance monitoring** — Track subnet ownership and configuration changes over time by querying at different block hashes.
- **Emission modeling** — Analyze how emissions are distributed across subnets and predict future allocation changes.
- **Capacity planning** — Identify subnets nearing their `max_n` limit to anticipate registration competition.

## Related Methods

- [`subnetInfo_getSubnetInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetInfo) — Get configuration info for a single subnet
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic info (emissions, registration costs) for a specific subnet
- [`subnetInfo_getAllDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllDynamicInfo) — Get dynamic info for all subnets
- [`subnetInfo_getSubnetHyperparams`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetHyperparams) — Get hyperparameters for a specific subnet
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) — Get the metagraph for a specific subnet
- [`subnetInfo_getLockCost`](https://www.dwellir.com/docs/bittensor/subnetInfo_getLockCost) — Get the TAO lock cost for creating a new subnet

---

## subnetInfo_getSubnetsInfo_v2 - Bittensor RPC Method

# subnetInfo_getSubnetsInfo_v2 - Bittensor RPC Method

## Overview

The `subnetInfo_getSubnetsInfo_v2` method returns the newer subnet catalog format for the full Bittensor network. Like `subnetInfo_getSubnetsInfo`, it gives you a network-wide view of active subnets, but it is designed for newer runtimes that package more subnet state into a single SCALE payload.

Use this method when you need a single request that can drive subnet discovery, analytics backends, or control-plane tooling. It is especially useful when you want to compare many subnets at once instead of making one request per subnet.

## SCALE Decoding

The shared Dwellir endpoint returns this payload as a JSON byte array, not as a hex string. Decode it with the Bittensor type registry that matches the target runtime.

**Using `bittensor` Python SDK:** Use the SDK when you want decoded subnet information without manually working through SCALE bytes. The SDK is the most practical option for dashboards, subnet catalogs, and analytics tooling.

**Using `@polkadot/api`:** Register the relevant Bittensor subnet info types before decoding the returned byte array. This is the better fit when you are already running a TypeScript service that consumes multiple Bittensor RPC methods.

**When to prefer `v2`:**

- Use `subnetInfo_getSubnetsInfo_v2` when your tooling expects the newer subnet-info layout.
- Use `subnetInfo_getSubnetsInfo` when you need the older layout or when your decoder is already wired to the legacy type definitions.
- Use `subnetInfo_getDynamicInfo` when you only need one subnet's live economics instead of the full catalog.

## Code Examples

## Request Parameters

- `at` (`DATA, optional`): Optional block hash used as state reference.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSubnetsInfo_v2",
    "params": [null],
    "id": 1
  }'
```

## Response Fields

- `result` (`DATA, required`): `DATA` - SCALE-encoded subnet info in v2 format.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x55aa77cc"
}
```

### Query with Python

```python
import requests

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'subnetInfo_getSubnetsInfo_v2',
    'params': [None],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

scale_bytes = result['result']
print(f'Subnet catalog payload size: {len(scale_bytes)} bytes')

# Decode with a Bittensor-aware type registry or SDK.
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "subnetInfo_getSubnetsInfo_v2",
    "params": [null],
    "id": 1
  }'
```

## Common Use Cases

- **Subnet discovery** — Build a current list of active Bittensor subnets and their associated metadata.
- **Analytics backends** — Snapshot network-wide subnet state before deeper per-subnet processing.
- **Explorer infrastructure** — Power subnet overview pages without making dozens of individual requests.
- **Historical comparisons** — Query the catalog at a specific block hash to compare how the subnet set changed over time.

## Related Methods

- [`subnetInfo_getSubnetsInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getSubnetsInfo) -- Legacy subnet catalog method
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) -- Dynamic economics for a single subnet
- [`subnetInfo_getMetagraph`](https://www.dwellir.com/docs/bittensor/subnetInfo_getMetagraph) -- Full neuron topology for a specific subnet

---

## subnetInfo_getSubnetState - JSON-RPC Method

# subnetInfo_getSubnetState - JSON-RPC Method

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns subnet state for `netuid`.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getSubnetState",
  "params": [
    1,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded subnet state, including live registration and validator data.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    1,
    4,
    1,
    4
  ]
}
```

---

## subnetInfo_getSubnetToPrune - JSON-RPC Method

# subnetInfo_getSubnetToPrune - JSON-RPC Method

## Description

Bittensor‑specific APIs to inspect subnets: hyperparameters, dynamic state, metagraphs, mechagraphs, and pruning hints. Use to build subnet dashboards and analytics.

Returns an optional netuid scheduled to prune.

## Code Examples

## Request Parameters

- `at` (`string, optional`): Optional block hash to query historical subnet state.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "subnetInfo_getSubnetToPrune",
  "params": [
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`number, required`): Netuid selected for pruning at the requested state.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 70
}
```

---

## subscribe_newHead - Bittensor RPC Method

# subscribe_newHead - Bittensor RPC Method

Legacy alias for `chain_subscribeNewHeads`. Subscribes to new block headers via a WebSocket connection, emitting a notification each time a new best block is imported. Modern clients should use `chain_subscribeNewHeads` instead.

The initial JSON-RPC response returns a subscription ID. New-head payloads are delivered afterward as WebSocket notifications on that subscription.

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "subscribe_newHead",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`string, required`): Subscription ID for the newly created legacy subscription.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "subscribe_newHead",
    "params": [],
    "id": 1
  }'
```

## Use Cases

- **Legacy client compatibility** -- Support older clients or libraries that use this alias name.
- **Real-time block tracking** -- Receive notifications for each new best block header as it is imported.

## Notes

- This is a legacy alias. Modern clients should use `chain_subscribeNewHeads`.
- Cancel with `unsubscribe_newHead` or `chain_unsubscribeNewHeads`.
- WebSocket-only; not available over HTTP.
- Dwellir-hosted Bittensor endpoints currently return `-32603 Internal error` for this legacy alias in live checks, so prefer `chain_subscribeNewHeads` for production integrations.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Canonical method for subscribing to new heads
- [`unsubscribe_newHead`](https://www.dwellir.com/docs/bittensor/unsubscribe_newHead) -- Cancel this subscription (legacy alias)
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Cancel via canonical name
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeFinalizedHeads) -- Subscribe to finalized headers instead
- [`chainHead_v1_follow`](https://www.dwellir.com/docs/bittensor/chainHead_v1_follow) -- Modern v2 chain head follow API

---

## swap_currentAlphaPrice - Bittensor RPC Method

# swap_currentAlphaPrice - Bittensor RPC Method

## Overview

The `swap_currentAlphaPrice` method returns the current alpha token price for swap calculations on the Bittensor network. In Bittensor's dynamic TAO system, each subnet has its own alpha token that can be swapped for TAO through an automated market maker (AMM) mechanism.

The alpha price represents the current exchange rate between a subnet's alpha token and TAO. This price is determined by the subnet's AMM pool state (alpha reserves and TAO reserves) and changes dynamically as swaps occur.

> **Note:** The shared Dwellir Bittensor endpoint expects a `netuid` parameter for this call. Handle unsupported subnets or runtime-specific failures as ordinary RPC errors instead of assuming a `null` response contract.

## Understanding Alpha Tokens and Dynamic TAO

In Bittensor's dynamic TAO system, each subnet operates its own internal token called "alpha." This creates a market-driven mechanism for valuing subnets:

- **Alpha tokens** represent stake within a specific subnet. When you stake TAO on a dynamic subnet, you receive alpha tokens at the current exchange rate
- **AMM pool:** Each subnet has an automated market maker pool containing reserves of both alpha tokens and TAO. The ratio of these reserves determines the price
- **Constant product formula:** The pool uses `k = alpha_in * tao_in` (similar to Uniswap v2). When TAO flows in, alpha flows out, and vice versa
- **Price impact:** Large swaps move the price more than small ones due to the constant product formula. The slippage depends on the pool depth
- **Emissions effect:** As the subnet earns emissions, TAO flows into the pool, which tends to increase the alpha price over time for well-performing subnets
- **Staking through swaps:** Staking TAO on a dynamic subnet effectively swaps TAO for alpha tokens. The alpha tokens represent your share of the subnet's stake pool

## Code Examples

### Using SubstrateExamples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "swap_currentAlphaPrice",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`Number, required`): Current ALPHA price as returned by the swap pallet.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 1.2
}
```

### Query with Python

```python
import requests
import json

url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

payload = {
    'jsonrpc': '2.0',
    'method': 'swap_currentAlphaPrice',
    'params': [1],
    'id': 1
}

response = requests.post(url, json=payload)
result = response.json()

if result.get('result') is not None:
    alpha_price = result['result']
    print(f'Current alpha price: {alpha_price}')
else:
    print('Alpha price not available (swap pallet may be disabled for this subnet)')

# For subnet-specific pricing, use subnetInfo_getDynamicInfo:
# payload = {
#     'jsonrpc': '2.0',
#     'method': 'subnetInfo_getDynamicInfo',
#     'params': [1, None],
#     'id': 2
# }
# response = requests.post(url, json=payload)
# # Decode SCALE result to get price field
```

### Full Python alpha token analysis

```python
import bittensor as bt
import requests
import json

sub = bt.subtensor(network='finney')
url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

# Get alpha price from swap RPC
payload = {
    'jsonrpc': '2.0',
    'method': 'swap_currentAlphaPrice',
    'params': [1],
    'id': 1
}
response = requests.post(url, json=payload)
swap_result = response.json()

if swap_result.get('result') is not None:
    print(f"Alpha price (swap RPC): {swap_result['result']}")

# Get detailed alpha pricing from dynamic info for each subnet
netuids = sub.get_all_subnet_netuids()

print(f"\n{'Netuid':>6} {'Dynamic':>8} {'Alpha Price':>14} {'TAO in Pool':>14} {'Alpha in Pool':>14}")
print("-" * 60)

for netuid in netuids[:20]:  # First 20 subnets
    try:
        dyn = sub.get_subnet_dynamic_info(netuid=netuid)
        if dyn and dyn.is_dynamic:
            tao_pool = dyn.tao_in / 1e9
            alpha_pool = dyn.alpha_in / 1e9
            price = tao_pool / alpha_pool if alpha_pool > 0 else 0
            print(f"{netuid:>6} {'Yes':>8} {price:>13,.6f} {tao_pool:>13,.2f} {alpha_pool:>13,.2f}")
        else:
            print(f"{netuid:>6} {'No':>8} {'N/A':>14} {'N/A':>14} {'N/A':>14}")
    except Exception as e:
        print(f"{netuid:>6} Error: {e}")

# Estimate swap output: if you want to swap X TAO for alpha
def estimate_swap_tao_for_alpha(tao_amount, tao_in_pool, alpha_in_pool):
    """Constant product AMM: (tao_in + delta) * (alpha_in - output) = k"""
    k = tao_in_pool * alpha_in_pool
    new_tao = tao_in_pool + tao_amount
    new_alpha = k / new_tao
    alpha_output = alpha_in_pool - new_alpha
    effective_price = tao_amount / alpha_output if alpha_output > 0 else 0
    slippage = (effective_price / (tao_in_pool / alpha_in_pool) - 1) * 100
    return alpha_output, effective_price, slippage

# Example: estimate swapping 100 TAO for alpha on subnet 1
dyn = sub.get_subnet_dynamic_info(netuid=1)
if dyn and dyn.is_dynamic:
    tao_pool = dyn.tao_in / 1e9
    alpha_pool = dyn.alpha_in / 1e9
    swap_amount = 100  # TAO

    alpha_out, eff_price, slippage = estimate_swap_tao_for_alpha(swap_amount, tao_pool, alpha_pool)
    spot_price = tao_pool / alpha_pool

    print(f"\n=== Swap Estimate: {swap_amount} TAO -> Alpha (Subnet 1) ===")
    print(f"Spot price: {spot_price:.6f} TAO/alpha")
    print(f"Alpha received: {alpha_out:,.4f}")
    print(f"Effective price: {eff_price:.6f} TAO/alpha")
    print(f"Price impact: {slippage:.2f}%")
```

### Query with JavaScript

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

try {
  const alphaPrice = await api.rpc.swap.currentAlphaPrice(1);
  console.log('Current alpha price:', alphaPrice.toString());
} catch (err) {
  console.log('Swap RPC not available on this node');
}

await api.disconnect();
```

### Query with cURL

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "method": "swap_currentAlphaPrice",
    "params": [1],
    "id": 1
  }'
```

## Common Use Cases

- **Subnet token pricing** — Display real-time alpha token prices for a specific subnet that uses dynamic TAO pricing.
- **Trading analysis** — Monitor alpha price movements to identify trading opportunities between TAO and subnet alpha tokens.
- **Swap UX previews** — Show users the current exchange rate before they execute a TAO-to-alpha or alpha-to-TAO swap.
- **Portfolio valuation** — Calculate the TAO-equivalent value of alpha token holdings across multiple subnets.
- **Dashboard widgets** — Display live price feeds for subnet tokens alongside other network metrics.
- **Slippage estimation** — Combine the alpha price with pool depth data from `getDynamicInfo` to estimate price impact for planned swaps.
- **Arbitrage detection** — Compare alpha prices across subnets to identify potential arbitrage opportunities.

## Related Methods

- [`swap_simSwapTaoForAlpha`](https://www.dwellir.com/docs/bittensor/swap_simSwapTaoForAlpha) — Simulate swapping TAO for alpha tokens (get estimated output)
- [`swap_simSwapAlphaForTao`](https://www.dwellir.com/docs/bittensor/swap_simSwapAlphaForTao) — Simulate swapping alpha tokens for TAO
- [`subnetInfo_getDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getDynamicInfo) — Get dynamic info including AMM pool state and alpha pricing
- [`subnetInfo_getAllDynamicInfo`](https://www.dwellir.com/docs/bittensor/subnetInfo_getAllDynamicInfo) — Get dynamic info for all subnets
- [`delegateInfo_getDelegates`](https://www.dwellir.com/docs/bittensor/delegateInfo_getDelegates) — Get all delegates (for staking yield comparison)

---

## swap_simSwapAlphaForTao - JSON-RPC Method

# swap_simSwapAlphaForTao - JSON-RPC Method

## Description

Bittensor swap helpers for TAO/ALPHA (price/simulation). Useful for quoting or UX previews; may be disabled on some nodes.

Simulates a swap from ALPHA to TAO. May return null if disabled.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `alphaAmount` (`u64, required`): Raw ALPHA amount to simulate, expressed in the smallest on-chain unit.
- `at` (`string, optional`): Optional block hash to query historical liquidity.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "swap_simSwapAlphaForTao",
  "params": [
    1,
    1000000000,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded simulation output for the ALPHA-to-TAO quote.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    7,
    122,
    166,
    0
  ]
}
```

## Error Responses

### Invalid Params

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": "invalid type: string \"1000000000\", expected u64 at line 1 column 12"
  }
}
```

---

## swap_simSwapTaoForAlpha - JSON-RPC Method

# swap_simSwapTaoForAlpha - JSON-RPC Method

## Description

Bittensor swap helpers for TAO/ALPHA (price/simulation). Useful for quoting or UX previews; may be disabled on some nodes.

Simulates a swap from TAO to ALPHA. May return null if disabled or illiquid.

## Code Examples

## Request Parameters

- `netuid` (`number, required`): Numeric subnet identifier.
- `taoAmount` (`u64, required`): Raw TAO amount to simulate, expressed in the smallest on-chain unit.
- `at` (`string, optional`): Optional block hash to query historical liquidity.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "swap_simSwapTaoForAlpha",
  "params": [
    1,
    1000000000,
    null
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): SCALE-encoded simulation output for the TAO-to-ALPHA quote.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    5,
    27,
    147,
    59
  ]
}
```

## Error Responses

### Invalid Params

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": "invalid type: string \"1000000000\", expected u64 at line 1 column 12"
  }
}
```

---

## system_accountNextIndex - Bittensor RPC Method

# system_accountNextIndex - Bittensor RPC Method

Returns the next valid transaction index (nonce) for an account on Bittensor. Unlike reading the nonce directly from storage, this method accounts for pending transactions in the transaction pool, giving you the correct nonce for the next transaction you want to submit.

## Code Examples

## Request Parameters

- `accountId` (`DATA, required`): Account identifier for which to fetch the next nonce.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_accountNextIndex",
    "params": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
    "id": 1
  }'
```

## Response Fields

- `result` (`INTEGER, required`): Next valid nonce (transaction index) for the account.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 15
}
```

## Use Cases

- **Transaction construction** -- Get the correct nonce before signing and submitting extrinsics (transfers, staking, subnet registrations, etc.) on Bittensor.
- **Sequential transaction submission** -- When sending multiple transactions in sequence, use this as the starting nonce and increment manually for each additional transaction.
- **Pool-aware nonce** -- Avoid "nonce too low" or "future transaction" errors by using a nonce that accounts for unconfirmed pool transactions.

## Notes

- This is the canonical method. `account_nextIndex` is an alias that returns the same result.
- The nonce includes pending pool transactions. If those transactions are dropped, the effective nonce may change.
- For high-throughput submission, consider manually tracking nonces rather than querying between each transaction.
- The account can be specified as an SS58 address or a raw hex-encoded AccountId.

## Related Methods

- [`account_nextIndex`](https://www.dwellir.com/docs/bittensor/account_nextIndex) -- Alias for this method
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Submit a signed extrinsic using the nonce
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- View pending transactions in the pool
- [`state_getStorage`](https://www.dwellir.com/docs/bittensor/state_getStorage) -- Read the on-chain nonce from `System.Account` (does not include pool)

---

## system_addLogFilter - Bittensor RPC Method

# system_addLogFilter - Bittensor RPC Method

Adds a log filter directive to the running Bittensor node, modifying its logging output in real time. Log filters use the same syntax as the `RUST_LOG` environment variable (e.g. `"sync=debug"`, `"runtime=trace"`). This is an administrative method that requires `--rpc-methods unsafe` and is not available on public shared RPC endpoints.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_addLogFilter",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`BOOLEAN, required`): Whether the log filter was applied successfully.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Runtime debugging** -- Temporarily increase log verbosity for specific modules to diagnose issues on a self-hosted Bittensor node.
- **Performance investigation** -- Enable trace-level logging for block import or consensus modules to investigate performance bottlenecks.
- **Selective logging** -- Add targeted filters for specific pallets or subsystems without restarting the node.

## Notes

- This is an unsafe/administrative method. Disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- Filter directives follow the `env_logger`/`RUST_LOG` format: `target=level` (e.g. `"grandpa=trace"`, `"txpool=debug"`).
- Use `system_resetLogFilter` to revert to default logging levels.
- Adding very verbose filters (trace/debug) can significantly impact node performance.

## Related Methods

- [`system_resetLogFilter`](https://www.dwellir.com/docs/bittensor/system_resetLogFilter) -- Reset log filters to defaults
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Check node roles
- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) -- Check sync state for debugging

---

## system_addReservedPeer - Bittensor RPC Method

# system_addReservedPeer - Bittensor RPC Method

Adds a reserved peer to the running Bittensor node. Reserved peers are always maintained as connections -- the node will actively try to connect and reconnect to them, and they are not subject to normal peer eviction. This is an administrative method that requires `--rpc-methods unsafe`.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_addReservedPeer",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`BOOLEAN, required`): Whether the peer was added successfully.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Guaranteed connectivity** -- Ensure your Bittensor node always maintains a connection to critical peers (e.g. your own boot nodes or trusted validators).
- **Private network setup** -- Build a private or semi-private node cluster by reserving connections between known nodes.
- **Connectivity recovery** -- Add a known-good peer when a node is struggling to find peers.

## Notes

- This is an unsafe/administrative method. Disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- Reserved peers are persistent -- the node will continue trying to connect to them even after disconnections.
- The peer multiaddr must include the peer ID (the `/p2p/12D3KooW...` component).
- Use `system_removeReservedPeer` to remove a peer from the reserved list.

## Related Methods

- [`system_removeReservedPeer`](https://www.dwellir.com/docs/bittensor/system_removeReservedPeer) -- Remove a reserved peer
- [`system_reservedPeers`](https://www.dwellir.com/docs/bittensor/system_reservedPeers) -- List current reserved peers
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List all currently connected peers
- [`system_localListenAddresses`](https://www.dwellir.com/docs/bittensor/system_localListenAddresses) -- Get the node's own listen addresses

---

## system_chain - Bittensor RPC Method

Returns the chain name of the Bittensor network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`system_chain` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Network Verification** -- Confirm your application is connected to the correct Bittensor network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Bittensor"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', 'Bittensor');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/bittensor/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bittensor/rpc_methods) -- List all available RPC methods

---

## system_chainType - JSON-RPC Method

# system_chainType - JSON-RPC Method

## Description

Returns the network environment type reported by the node (for example, `Development`, `Local`, or `Live`).

When to use it:

- Gate behavior by environment (e.g., hide faucets or disable dangerous actions on `Live`).
- Verify you are connected to the expected environment in CI, staging, or production.
- Surface readable environment context in UIs and logs.

It returns a short string such as `Live` for mainnet.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chainType",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): Node environment classification such as `Development`, `Local`, or `Live`.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Live"
}
```

---

## system_health - Bittensor RPC Method

# system_health - Bittensor RPC Method

Returns the health status of the Bittensor node, including peer count, sync state, and whether the node expects to have peers.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`system_health` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Bittensor
- **Load Balancing** - Route requests only to healthy, fully synced nodes for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Bittensor:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Bittensor nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) - Get chain name
- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) - Get detailed sync progress
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) - Get detailed peer information

---

## system_localListenAddresses - Bittensor RPC Method

# system_localListenAddresses - Bittensor RPC Method

Returns the list of multiaddr listen addresses that the Bittensor node is currently bound to. These are the addresses on which the node accepts incoming peer-to-peer connections. The response includes the protocol, IP address, port, and the node's peer ID.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_localListenAddresses",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`ARRAY, required`): List of multiaddr listen addresses configured on the node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": ["/ip4/127.0.0.1/tcp/30333"]
}
```

## Use Cases

- **Node configuration verification** -- Confirm which interfaces and ports the node is listening on after startup.
- **Peer sharing** -- Retrieve the node's full multiaddr (including peer ID) to share with other nodes for peering.
- **Network diagnostics** -- Verify that the node is bound to the expected addresses for firewall and networking troubleshooting.

## Notes

- On public shared RPC endpoints, the returned addresses may be masked or reflect the infrastructure's internal addresses.
- The multiaddr format includes protocol, address, port, and peer ID components.
- This method is safe (read-only) and available on most node configurations.

## Related Methods

- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List currently connected peers
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Get the node's configured roles
- [`system_addReservedPeer`](https://www.dwellir.com/docs/bittensor/system_addReservedPeer) -- Add a reserved peer using a multiaddr
- [`system_unstable_networkState`](https://www.dwellir.com/docs/bittensor/system_unstable_networkState) -- Detailed network diagnostics

---

## system_localPeerId - JSON-RPC Method

# system_localPeerId - JSON-RPC Method

## Description

Interact with Substrate JSON‑RPC. This method is commonly used to build reliable indexers, developer tooling, and responsive UIs.

Returns the libp2p PeerId of the node.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_localPeerId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`string, required`): libp2p PeerId advertised by the connected node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "12D3KooWKx3xLKxmyYYyL8yQ3J8A1tRmfjZGrJ43bXd7RbsKrTWd"
}
```

---

## system_name - Bittensor RPC Method

Returns the node implementation name on Bittensor. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`system_name` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Bittensor
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Bittensor node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bittensor node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Bittensor node implementation: {name}')

# system_name - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
name = substrate.rpc_request('system_name', [])['result']
print(f'Bittensor node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bittensor node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Bittensor nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bittensor/rpc_methods) -- List all available RPC methods

---

## system_nodeRoles - Bittensor RPC Method

# system_nodeRoles - Bittensor RPC Method

Returns the roles that the Bittensor node is configured with. Common roles include `Full` (stores and serves all chain data), `Authority` (participates in block production and consensus), and `LightClient` (only stores headers). This information helps determine the capabilities and behavior of the node you are connected to.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_nodeRoles",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`ARRAY, required`): Node role flags assigned to the current node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": ["authority", "full_node"]
}
```

## Use Cases

- **Node capability detection** -- Determine whether the connected node is a full node, an authority (validator), or a light client before making role-specific API calls.
- **Health monitoring** -- Verify that a validator node is running with the `Authority` role as expected.
- **Client behavior adaptation** -- Adjust your application's behavior based on the node type (e.g. skip certain queries on light clients).

## Notes

- On Dwellir's shared RPC endpoints, the node role is typically `Full`.
- Authority nodes participate in block production and may have different performance characteristics.
- This method is safe (read-only) and available on all node configurations.

## Related Methods

- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) -- Check the node's sync progress
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List connected peers
- [`system_localListenAddresses`](https://www.dwellir.com/docs/bittensor/system_localListenAddresses) -- Get listen addresses
- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node software version

---

## system_peers - Bittensor RPC Method

# system_peers - Bittensor RPC Method

Returns a list of peers the Bittensor node is currently connected to, including their peer ID, roles, best block information, and protocol details. This is useful for network monitoring, debugging connectivity issues, and understanding the node's position in the peer-to-peer network.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_peers",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`ARRAY, required`): Connected peers returned as peer objects.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Connectivity monitoring** -- Track the number and identity of connected peers to ensure the node is well-connected to the Bittensor network.
- **Sync diagnostics** -- Compare peer best block numbers to the node's own to diagnose sync issues.
- **Network topology** -- Build a view of the peer-to-peer network topology for monitoring dashboards.
- **Peer health checks** -- Detect when a node becomes isolated or has too few peers.

## Notes

- On public shared RPC endpoints, the peer list may be masked, truncated, or empty for security reasons.
- The method may require `--rpc-methods unsafe` on some node configurations.
- The peer count can fluctuate as connections are established and dropped.

## Related Methods

- [`system_localListenAddresses`](https://www.dwellir.com/docs/bittensor/system_localListenAddresses) -- Get the node's own listen addresses
- [`system_reservedPeers`](https://www.dwellir.com/docs/bittensor/system_reservedPeers) -- List reserved (pinned) peers
- [`system_addReservedPeer`](https://www.dwellir.com/docs/bittensor/system_addReservedPeer) -- Add a reserved peer
- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) -- Check the node's sync progress
- [`system_unstable_networkState`](https://www.dwellir.com/docs/bittensor/system_unstable_networkState) -- Detailed network state diagnostics

---

## system_properties - Bittensor RPC Method

Returns the chain-specific properties for Bittensor, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`system_properties` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Bittensor
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Current Dwellir Values

> **Current Dwellir values:** Bittensor mainnet currently reports `TAO`, `9 decimals`, and SS58 format `42`.

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bittensor/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bittensor/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bittensor/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/bittensor/rpc_methods) -- List all available RPC methods

---

## system_removeReservedPeer - Bittensor RPC Method

# system_removeReservedPeer - Bittensor RPC Method

Removes a peer from the reserved peer list on the running Bittensor node. After removal, the node will no longer actively maintain a connection to that peer, and the peer becomes subject to normal eviction rules. This is an administrative method that requires `--rpc-methods unsafe`.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_removeReservedPeer",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`BOOLEAN, required`): Whether the peer was removed from the reserved list successfully.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **Peer rotation** -- Remove decommissioned or misbehaving peers from the reserved list.
- **Network reconfiguration** -- Update the reserved peer set when changing your node infrastructure.
- **Troubleshooting** -- Remove a peer that is causing connectivity issues.

## Notes

- This is an unsafe/administrative method. Disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- Removing a reserved peer does not immediately disconnect it; it just removes the reservation, making the connection subject to normal peer management.
- The peer ID is the libp2p peer identifier, not a multiaddr.

## Related Methods

- [`system_addReservedPeer`](https://www.dwellir.com/docs/bittensor/system_addReservedPeer) -- Add a reserved peer
- [`system_reservedPeers`](https://www.dwellir.com/docs/bittensor/system_reservedPeers) -- List current reserved peers
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List all connected peers

---

## system_reservedPeers - Bittensor RPC Method

# system_reservedPeers - Bittensor RPC Method

Returns the list of reserved peers for the Bittensor node. Reserved peers are connections that the node actively maintains -- it will always try to stay connected to them and will not evict them during peer management. These are typically set via the `--reserved-nodes` CLI flag or the `system_addReservedPeer` RPC method.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_reservedPeers",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`ARRAY, required`): Reserved peers configured on the node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Use Cases

- **Configuration verification** -- Confirm that the expected reserved peers are configured after node startup or after adding/removing reserved peers via RPC.
- **Infrastructure monitoring** -- Track the reserved peer configuration across your Bittensor node fleet.
- **Networking diagnostics** -- Check which peers are reserved when debugging connectivity issues.

## Notes

- On public shared RPC endpoints, the reserved peer list may be empty or masked for security.
- This method may require `--rpc-methods unsafe` on some node configurations.
- Reserved peers are distinct from boot nodes; boot nodes are used for initial peer discovery, while reserved peers are permanently maintained connections.

## Related Methods

- [`system_addReservedPeer`](https://www.dwellir.com/docs/bittensor/system_addReservedPeer) -- Add a reserved peer
- [`system_removeReservedPeer`](https://www.dwellir.com/docs/bittensor/system_removeReservedPeer) -- Remove a reserved peer
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List all connected peers (not just reserved)
- [`system_localListenAddresses`](https://www.dwellir.com/docs/bittensor/system_localListenAddresses) -- Get the node's own listen addresses

---

## system_resetLogFilter - Bittensor RPC Method

# system_resetLogFilter - Bittensor RPC Method

Resets the node's log filter to its default configuration, removing any filters that were added at runtime via `system_addLogFilter`. This is an administrative method that requires `--rpc-methods unsafe` and is not available on public shared RPC endpoints.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_resetLogFilter",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`BOOLEAN, required`): Whether the log filter was reset successfully.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Use Cases

- **End debugging session** -- Reset log verbosity to defaults after a debugging session to restore normal log volume and node performance.
- **Reduce log noise** -- Remove verbose filters that were temporarily added for investigation.
- **Clean state** -- Ensure the log filter is in a known-good default state.

## Notes

- This is an unsafe/administrative method. Disabled on public RPC endpoints including Dwellir's shared Bittensor nodes.
- The default filter is determined by the node's startup configuration (`--log` CLI flag or `RUST_LOG` environment variable).
- Resetting the filter does not affect log output already written; it only changes future log filtering.

## Related Methods

- [`system_addLogFilter`](https://www.dwellir.com/docs/bittensor/system_addLogFilter) -- Add a log filter directive
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Check node roles
- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) -- Check sync progress

---

## system_syncState - Bittensor RPC Method

# system_syncState - Bittensor RPC Method

Returns the node's current synchronization progress, including the starting block, the current block, and the highest known block. This information is essential for determining whether a Bittensor node is fully synced and ready to serve accurate data.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_syncState",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`OBJECT, required`): Sync state for the node including start, current, and highest values.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "startingBlock": 900000,
    "currentBlock": 915000,
    "highestBlock": 930000
  }
}
```

## Use Cases

- **Health checks** -- Verify that a Bittensor node is fully synced before routing traffic to it. A node with `currentBlock` far below `highestBlock` is still catching up.
- **Sync progress display** -- Show sync progress bars in admin dashboards or monitoring UIs.
- **Load balancer gating** -- Only add a node to the load balancer pool once `currentBlock >= highestBlock - threshold`.
- **Alert triggers** -- Fire alerts when a node falls behind by more than a configurable number of blocks.

## Notes

- A fully synced node will have `currentBlock` equal to or very close to `highestBlock` (within 1-2 blocks).
- During warp sync, `startingBlock` may be a recent block rather than 0.
- This method is safe (read-only) and available on all node configurations.

## Related Methods

- [`system_health`](https://www.dwellir.com/docs/bittensor/system_health) -- Quick boolean check for sync and peer status
- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- List connected peers and their best block numbers
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Get the node's configured roles
- [`chain_getHeader`](https://www.dwellir.com/docs/bittensor/chain_getHeader) -- Get the latest block header

---

## system_unstable_networkState - Bittensor RPC Method

# system_unstable_networkState - Bittensor RPC Method

Returns detailed diagnostic information about the node's network layer, including the peer ID, listen addresses, connected peers with protocol details, and notification protocols. This method is marked as unstable, meaning its response format may change between node versions without notice.

## Code Examples

## Request Parameters

This method accepts no parameters.

## Request Example

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_unstable_networkState",
    "params": [],
    "id": 1
  }'
```

## Response Fields

- `result` (`OBJECT, required`): Network diagnostics and transport state returned by this unstable API.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 12,
    "isSyncing": false
  }
}
```

## Use Cases

- **Deep network diagnostics** -- Inspect protocol-level details of peer connections, including which notification protocols are negotiated.
- **NAT and firewall debugging** -- Compare `listenedAddresses` with `externalAddresses` to detect NAT issues or misconfigured firewalls.
- **Peer protocol analysis** -- Determine which peers support which protocols (block announces, transactions, GRANDPA, etc.).

## Notes

- This API is explicitly marked as unstable. The response format may change without deprecation warnings between Substrate/Bittensor node versions.
- The response can be very large on well-connected nodes.
- May require `--rpc-methods unsafe` on some node configurations.
- On public shared RPC endpoints, the response may be masked or limited.

## Related Methods

- [`system_peers`](https://www.dwellir.com/docs/bittensor/system_peers) -- Simpler peer list with basic info
- [`system_localListenAddresses`](https://www.dwellir.com/docs/bittensor/system_localListenAddresses) -- Just the listen addresses
- [`system_syncState`](https://www.dwellir.com/docs/bittensor/system_syncState) -- Sync progress information
- [`system_nodeRoles`](https://www.dwellir.com/docs/bittensor/system_nodeRoles) -- Node role information

---

## system_version - Bittensor RPC Method

Returns the node implementation version string on Bittensor. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`system_version` is essential for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Bittensor
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for decentralized AI inference, subnet-specific AI models, TAO staking, and cross-subnet AI collaboration
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Bittensor node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bittensor node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Bittensor node version: {version}')

# system_version - Bittensor RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY')
version = substrate.rpc_request('system_version', [])['result']
print(f'Bittensor node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bittensor node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Bittensor nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bittensor/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bittensor/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/bittensor/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bittensor/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/bittensor/rpc_methods) -- List all available RPC methods

---

## transaction_v1_broadcast - Bittensor RPC Method

# transaction_v1_broadcast - Bittensor RPC Method

Broadcasts a signed, SCALE-encoded transaction to the Bittensor network via the new JSON-RPC v2 transaction API. Unlike the legacy `author_submitExtrinsic`, this method is designed for fire-and-forget submission -- it broadcasts the transaction to peers without waiting for pool validation results. Use `transactionWatch_v1_submitAndWatch` if you need to track the transaction lifecycle.

## Code Examples

## Request Parameters

- `transaction` (`string, required`): Hex-encoded SCALE-encoded signed transaction.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "transaction_v1_broadcast",
  "params": [
    "<transaction>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`null, required`): Acknowledges the transaction has been accepted for broadcast. Does not guarantee inclusion in a block.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Transaction submission** -- Broadcast signed Bittensor transactions (TAO transfers, subnet registrations, staking operations) to the network.
- **Fire-and-forget** -- Submit transactions without maintaining a WebSocket connection to track their status.
- **High-throughput submission** -- Broadcast many transactions quickly without waiting for individual pool validation responses.

## Notes

- This method is part of the new JSON-RPC v2 specification and may not be available on all node configurations.
- A successful response only means the transaction was accepted for broadcast. It does not guarantee pool acceptance or block inclusion.
- For transaction lifecycle tracking, use `transactionWatch_v1_submitAndWatch` instead.
- The transaction must be properly signed with a valid nonce. Use `system_accountNextIndex` to get the correct nonce.

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Legacy method to submit and validate a transaction
- [`transactionWatch_v1_unwatch`](https://www.dwellir.com/docs/bittensor/transactionWatch_v1_unwatch) -- Stop watching a transaction submitted via the watch API
- [`system_accountNextIndex`](https://www.dwellir.com/docs/bittensor/system_accountNextIndex) -- Get the correct nonce for transaction construction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bittensor/author_pendingExtrinsics) -- View pending transactions in the pool

---

## transaction_v1_stop - JSON-RPC Method

# transaction_v1_stop - JSON-RPC Method

## Description

Interact with Substrate JSON‑RPC. This method is commonly used to build reliable indexers, developer tooling, and responsive UIs.

Stops a transaction processing job started previously.

## Code Examples

## Request Parameters

- `operationId` (`string, required`): Opaque operation ID returned by `transaction_v1_broadcast`.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "transaction_v1_stop",
  "params": [
    "<operationId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`null, required`): Confirms the broadcast operation is no longer being retried by the node.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": null
}
```

## Error Responses

### Invalid Operation ID

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid operation id"
  }
}
```

---

## transactionWatch_v1_submitAndWatch - JSON-...

# transactionWatch_v1_submitAndWatch - JSON-...

Submits a signed extrinsic and returns a subscription ID you can use to follow
validation, inclusion, and finalization updates. Public endpoints may reject
this method when transaction submission is disabled.

## Code Examples

## Request Parameters

- `transaction` (`string, required`): Hex-encoded SCALE extrinsic to submit and watch.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "transactionWatch_v1_submitAndWatch",
    "params": ["0xSIGNED_EXTRINSIC_HEX"],
    "id": 1
  }'
```

## Response Fields

- `result` (`string, required`): Opaque subscription ID used in subsequent `transactionWatch_v1_watchEvent` notifications and when calling `transactionWatch_v1_unwatch`.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<subscriptionId>"
}
```

## Error Responses

### Invalid Params

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params"
  }
}
```

---

## transactionWatch_v1_unwatch - Bittensor RPC Method

# transactionWatch_v1_unwatch - Bittensor RPC Method

Cancels a transaction watch subscription that was started with `transactionWatch_v1_submitAndWatch`. After calling this method, no further transaction lifecycle notifications will be delivered for that subscription. Always call this method when you are done tracking a transaction to free server-side resources.

## Code Examples

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by `transactionWatch_v1_submitAndWatch`.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "transactionWatch_v1_unwatch",
  "params": [
    "<subscriptionId>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`null, required`): Confirms the watch subscription has been cancelled.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Use Cases

- **Subscription cleanup** -- Cancel a transaction watch after the transaction has been finalized or after you have received the status you were waiting for.
- **Timeout handling** -- Stop watching a transaction that has not been included in a block within your expected timeframe.
- **Resource management** -- Free server-side resources in applications that submit many transactions.

## Notes

- This method is part of the new JSON-RPC v2 specification and operates over WebSocket connections.
- Always unwatch when done. Leaving subscriptions open consumes server memory and may count against rate limits.
- If the transaction has already been finalized or dropped, the subscription may already be inactive, but calling `unwatch` is still safe.

## Related Methods

- [`transaction_v1_broadcast`](https://www.dwellir.com/docs/bittensor/transaction_v1_broadcast) -- Broadcast a transaction without watching (fire-and-forget)
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitAndWatchExtrinsic) -- Legacy method to submit and watch a transaction
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bittensor/author_submitExtrinsic) -- Legacy submission without watching

---

## unsubscribe_newHead - Bittensor RPC Method

# unsubscribe_newHead - Bittensor RPC Method

Legacy alias for `chain_unsubscribeNewHeads`. Cancels a WebSocket subscription that was started with `subscribe_newHead` (or `chain_subscribeNewHeads`). Provide the subscription ID that was returned when the subscription was created. Modern clients should use `chain_unsubscribeNewHeads` instead.

## Request Parameters

- `subscriptionId` (`string, required`): The subscription ID returned by the subscribe call.

## Request Example

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "unsubscribe_newHead",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Response Fields

- `result` (`boolean, required`): `true` if successfully cancelled, `false` if the ID was not found.

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

> This method requires a WebSocket connection and is not available over HTTP.

### WebSocket (wscat)

```bash
wscat -c wss://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -x '{
    "jsonrpc": "2.0",
    "method": "unsubscribe_newHead",
    "params": ["SUBSCRIPTION_ID"],
    "id": 1
  }'
```

## Use Cases

- **Legacy client compatibility** -- Support older clients or libraries that use this alias name for unsubscribing.
- **Subscription cleanup** -- Cancel new-head notifications when no longer needed.

## Notes

- This is a legacy alias. Modern clients should use `chain_unsubscribeNewHeads`.
- Always unsubscribe when done to free server-side resources.
- WebSocket-only; not available over HTTP.

## Related Methods

- [`subscribe_newHead`](https://www.dwellir.com/docs/bittensor/subscribe_newHead) -- Start the subscription this method cancels (legacy alias)
- [`chain_unsubscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeNewHeads) -- Canonical unsubscribe method
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bittensor/chain_subscribeNewHeads) -- Canonical subscribe method
- [`chain_unsubscribeFinalizedHeads`](https://www.dwellir.com/docs/bittensor/chain_unsubscribeFinalizedHeads) -- Cancel a finalized-heads subscription

---

## web3_clientVersion - Bittensor RPC Method

Returns the current client software version string for your Bittensor node, including the client name, version number, OS, and runtime.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

## When to Use This Method

`web3_clientVersion` is valuable for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Bittensor client:', clientVersion);

// Using fetch
const response = await fetch('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Bittensor client: {client_version}')

# web3_clientVersion - Bittensor RPC Method
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Bittensor client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/bittensor/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/bittensor/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/bittensor/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/bittensor/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Bittensor RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Bittensor.

> **Why Bittensor?** Build on the decentralized machine intelligence network built around subnets, TAO staking, and validator-miner coordination with Yuma Consensus, subnet-based specialization, dual Substrate and EVM surfaces, and onchain incentive coordination.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for AI/ML developers, subnet operators, and teams building decentralized machine learning applications:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Bittensor RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-bittensor-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/bittensor/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/bittensor/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/bittensor/web3_clientVersion) - Get node client version

---

## Blast RPC with Dwellir

## Why Build on Blast?

Blast is a next-generation Ethereum Layer 2 solution designed for high-performance applications and DeFi protocols. Built to optimize developer and user experience, Blast offers:

### **High-Performance EVM**

- **Fast transaction finality** - Sub-second confirmation times
- **Low gas costs** - Significantly reduced fees compared to Ethereum L1
- **High throughput** - Optimized for demanding applications

### **Native Yield Generation**

- **Built-in yield** - ETH and USDB automatically earn yield
- **Developer incentives** - Gas fee rebates and yield sharing
- **Capital efficiency** - Maximize returns for users and protocols

### **Developer-First Design**

- **Full EVM compatibility** - Deploy existing Ethereum contracts seamlessly
- **Rich tooling support** - Works with Hardhat, Foundry, and all standard tools
- **Growing ecosystem** - Active developer community and partnerships

### **Decentralized & Secure**

- **Ethereum security** - Inherits L1 security properties
- **Decentralized sequencing** - Robust network architecture
- **Battle-tested infrastructure** - Proven reliability and uptime

## Quick Start with Blast

Connect to Blast in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem
Web3.py

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Blast mainnet
const provider = new JsonRpcProvider(
  'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Blast mainnet
const web3 = new Web3(
  'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Blast:', chainId === 81457n);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';
import { blast } from 'viem/chains';

// Create Blast client
const client = createPublicClient({
  chain: blast,
  transport: http('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

```python
from web3 import Web3

# Blast RPC with Dwellir
w3 = Web3(Web3.HTTPProvider(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
))

# Verify connection
print(f'Connected: {w3.is_connected()}')
print(f'Chain ID: {w3.eth.chain_id}')

# Get latest block number
latest_block = w3.eth.block_number
print(f'Latest block: {latest_block}')

# Get account balance
balance = w3.eth.get_balance('0x...')
print(f'Balance: {w3.from_wei(balance, "ether")} ETH')
```

## Network Information

| Parameter        | Value     | Details         |
| ---------------- | --------- | --------------- |
| Chain ID         | 81457     | Mainnet         |
| Testnet Chain ID | 168587773 | Sepolia Testnet |
| Gas Token        | ETH       | Native token    |
| RPC Standard     | Ethereum  | JSON-RPC 2.0    |

## API Reference

Blast supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/).

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // Check transaction status
  if (receipt.status === 1) {
    console.log('Transaction confirmed:', receipt.transactionHash);
  }

  return receipt;
}
```

### Gas Optimization

Optimize gas costs on Blast:

```javascript
// Estimate gas for transaction
const gasEstimate = await provider.estimateGas(tx);

// Get current fee data for EIP-1559
const feeData = await provider.getFeeData();

// Prepare transaction with optimal gas settings
const optimizedTx = {
  ...tx,
  gasLimit: gasEstimate,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
};
```

### Event Filtering

Efficiently query contract events:

```javascript
// Query events with automatic retry and pagination
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 2000; // Recommended batch size

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);
  }

  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine multiple RPC calls for optimal performance:

```javascript
const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] }
];

const results = await provider.send(batch);
```

### 2. **Connection Pooling**

Reuse provider instances to minimize connection overhead:

```javascript
// Singleton pattern for provider
class BlastProvider {
  static instance = null;

  static getInstance() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
      );
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data to reduce API calls:

```javascript
const cache = new Map();

async function getCachedBlockData(blockNumber) {
  const key = `block_${blockNumber}`;

  if (!cache.has(key)) {
    const block = await provider.getBlock(blockNumber);
    cache.set(key, block);
  }

  return cache.get(key);
}
```

## Troubleshooting Common Issues

### Error: "Wrong chain ID"

Verify you're connecting to the correct Blast network:

```javascript
// Check current chain ID
const chainId = await provider.send('eth_chainId', []);
console.log('Current chain ID:', parseInt(chainId, 16));

// Blast Mainnet should return 81457 (0x13e31)
// Blast Sepolia Testnet should return 168587773 (0xa0c71fd)
```

### Error: "Transaction underpriced"

Blast uses EIP-1559 pricing. Always use dynamic gas pricing:

```javascript
// Get current fee data
const feeData = await provider.getFeeData();

const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: 21000n
};
```

### Error: "Rate limit exceeded"

Implement exponential backoff for resilient applications:

```javascript
async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 2 ** i * 1000));
      } else {
        throw error;
      }
    }
  }
}
```

## FAQs

### What makes Blast different from other L2s?

Blast is unique in providing native yield generation for ETH and USDB holdings, along with gas fee rebates for developers and automatic capital efficiency optimizations.

### How do I bridge assets to Blast?

Use the official Blast Bridge at [blast.io/bridge](https://blast.io/bridge) or integrate with supported bridging protocols.

### Does Blast support all Ethereum tools?

Yes, Blast is fully EVM-compatible and works with all standard Ethereum development tools including Hardhat, Foundry, Remix, and MetaMask.

## Smoke Tests

Test your connection to Blast:

### curl (Mainnet)

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

### curl (Testnet)

```bash
curl -X POST https://api-blast-sepolia-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

### Ethers.js v6

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
const network = await provider.getNetwork();

console.log('Block number:', blockNumber);
console.log('Chain ID:', network.chainId); // Should be 81457
```

### Web3.py

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

print(f'Connected: {w3.is_connected()}')
print(f'Chain ID: {w3.eth.chain_id}')  # Should be 81457
print(f'Latest block: {w3.eth.block_number}')
```

## Migration Guide

### From Ethereum Mainnet

Moving from L1 to Blast requires minimal changes:

```javascript
// Before (Ethereum)
const provider = new JsonRpcProvider('https://eth-rpc.example.com');

// After (Blast)
const provider = new JsonRpcProvider(
  'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'
);

// Smart contracts work identically
// Same tooling and libraries
// Note: Different chain ID (81457)
// Note: Separate block numbers
// Lower gas fees
// Native yield generation
```

## Resources & Tools

### Official Resources

- [Blast Documentation](https://docs.blast.io)
- [Blast Bridge](https://blast.io/bridge)
- [Blast Explorer](https://blastscan.io)

### Developer Tools

- [Blast GitHub](https://github.com/blast-io)
- [Developer Portal](https://docs.blast.io/building)

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

### Related Reading

- [Top 8 Blast RPC Providers 2026](https://www.dwellir.com/blog/best-blast-rpc-providers)

***

*Start building on Blast with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## debug_traceBlock - Blast RPC Method

Traces all transactions in a block on Blast by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Blast - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Blast
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Blast RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Blast:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Blast blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/blast/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/blast/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/blast/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/blast/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Blast RPC Method

Traces all transactions in a block on Blast identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Blast - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Blast
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Blast RPC Method
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Blast:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Blast:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/blast/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/blast/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/blast/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/blast/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/blast/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Blast RPC Method

Traces all transactions in a block on Blast identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Blast - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Blast execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Blast RPC Method
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Blast:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Blast:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/blast/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/blast/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/blast/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/blast/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Blast RPC Method

Traces a call on Blast without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Blast - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Blast
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Blast RPC Method
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
        "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
        "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    data: '0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    data: '0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    'data': '0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
        "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Blast to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Blast:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Blast:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/blast/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/blast/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/blast/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/blast/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Blast RPC Method

Traces a transaction execution on Blast by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Blast
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2'

# debug_traceTransaction - Blast RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/blast/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/blast/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Blast RPC Method

Returns a list of addresses owned by the client on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for DeFi developers, yield protocol builders, and teams building passive-income dApps in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/blast/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/blast/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Blast RPC Method

Returns the number of the most recent block on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_blockNumber` is fundamental for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Syncing Applications** - Keep your dApp in sync with the latest Blast blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Blast block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Blast block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Blast block: {block_number}')

# eth_blockNumber - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Blast block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Blast block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Blast:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Blast:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Blast node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/blast/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check if node is still syncing

---

## eth_call - Blast RPC Method

Executes a new message call immediately without creating a transaction on Blast. Used for reading smart contract state.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_call` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Blast
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';
const walletAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Blast. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Blast
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Blast

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Blast RPC Method
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "data": "0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    data := common.FromHex("0x70a08231000000000000000000000000A8b2218036Eab12e58e02f88E8825723aB4C5E5f")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Blast RPC Method

Returns the chain ID used for transaction signing on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_chainId` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Blast RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/blast/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check sync status

---

## eth_coinbase - Blast RPC Method

Checks the legacy `eth_coinbase` compatibility method on Blast. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for DeFi developers, yield protocol builders, and teams building passive-income dApps when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Blast coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Blast coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Blast coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Blast coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Blast coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Blast nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/blast/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/blast/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/blast/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Blast RPC Method

Estimates the gas necessary to execute a transaction on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Blast
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Blast.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Blast may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "to": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Blast RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/blast/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Blast RPC Method

Returns historical gas fee data on Blast, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Blast without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Blast.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Blast) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/blast/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/blast/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Blast RPC Method

Returns the current gas price on Blast in wei.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Blast
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Blast. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Blast, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Blast: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Blast supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Blast RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/blast/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/blast/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Blast RPC Method

Returns the balance of a given address on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getBalance` is fundamental for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Blast
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Blast
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Blast. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Blast`);
  return balance;
}

displayBalance('0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Blast at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Blast')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Blast')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Blast.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Blast RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/blast/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/blast/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Blast RPC Method

Returns information about a block by hash on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getBlockByHash` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Blast
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Blast.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Blast RPC Method
verify_block_still_canonical('0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Blast.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Blast RPC Method

Returns information about a block by block number on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getBlockByNumber` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Blast
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Blast with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Blast. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Blast RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/blast/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/blast/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Blast RPC Method

# eth_getBlockReceipts - Blast RPC Method

Returns all transaction receipts for a block on Blast. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getBlockReceipts` is useful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Blast, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Blast. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x645a6a1b48a2c3312d7bc389eb4f548acfbce860916e6894bd7b611d26f3de0f",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/blast/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/blast/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Blast RPC Method

Returns the bytecode at a given address on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getCode` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Blast
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Blast:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Blast:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Blast RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/blast/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/blast/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Blast RPC Method

Polls a filter on Blast and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getFilterChanges` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Blast
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Blast RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Blast:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/blast/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/blast/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/blast/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/blast/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/blast/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Blast RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Blast. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getFilterLogs` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Blast
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Blast RPC Method
FILTER_ID=$(curl -s -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/blast/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/blast/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/blast/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Blast RPC Method

# eth_getLogs - Blast RPC Method

Returns an array of all logs matching a given filter object on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Blast for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Blast
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Blast.

```javascript
const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Blast
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Blast

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/blast/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Blast RPC Method

Returns the value from a storage position at a given address on Blast. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getStorageAt` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Blast:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Blast:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/blast/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/blast/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/blast/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Blast RPC Method

# eth_getTransactionByHash - Blast RPC Method

Returns the information about a transaction by transaction hash on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getTransactionByHash` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Blast
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Blast. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Blast. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/blast/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Blast RPC Method

Returns the number of transactions sent from an address on Blast, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getTransactionCount` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Blast
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Blast nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f'
nonce = get_transaction_count(address)
print(f'Blast nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Blast nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Blast:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/blast/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/blast/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/blast/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Blast RPC Method

# eth_getTransactionReceipt - Blast RPC Method

Returns the receipt of a transaction by transaction hash on Blast. Receipt is only available for mined transactions.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_getTransactionReceipt` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Blast and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Blast. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Blast, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x56f0fad1416d7e1ce20a18fc488c0d850c24b0bd4297f51bb901eeb24346bbe2")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/blast/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/blast/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Blast RPC Method

Returns the legacy `eth_hashrate` compatibility value on Blast. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Blast hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Blast hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Blast hashrate: {hashrate} H/s')

# eth_hashrate - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Blast hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Blast hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/blast/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/blast/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Blast RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Blast. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Blast
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Blast priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Blast priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Blast priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Blast:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Blast:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/blast/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/blast/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/blast/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Blast RPC Method

Checks the legacy `eth_mining` compatibility method on Blast. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Blast mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Blast mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Blast mining: {mining["result"]}')

# eth_mining - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Blast mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Blast mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/blast/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/blast/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Blast RPC Method

Creates a filter on Blast that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_newBlockFilter` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Block Monitoring** - Detect new blocks on Blast as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Blast RPC Method
FILTER_ID=$(curl -s -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Blast:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/blast/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/blast/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Blast RPC Method

Creates a filter object on Blast based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_newFilter` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Event Monitoring** - Subscribe to specific contract events on Blast such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Blast RPC Method
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Blast:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/blast/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/blast/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/blast/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/blast/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Blast RPC Method

Creates a filter on Blast that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Mempool Monitoring** - Observe unconfirmed transactions on Blast to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for yield-generating dApps, DeFi protocols with built-in returns, and gas-subsidized applications

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Blast RPC Method
FILTER_ID=$(curl -s -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Blast...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Blast:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/blast/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/blast/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Blast RPC Method

Returns the current Ethereum protocol version used by the Blast node.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_protocolVersion` is useful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Blast protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Blast protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Blast protocol version: {version}')

# eth_protocolVersion - Blast RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
print(f'Blast protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Blast protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Blast nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/blast/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/blast/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Blast RPC Method

Submits a pre-signed transaction for broadcast to Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Blast
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Blast mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/blast/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Blast RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0xA8b2218036Eab12e58e02f88E8825723aB4C5E5f")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/blast/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/blast/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Blast RPC Method

Creates and sends a new transaction from an unlocked account on Blast. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for DeFi developers, yield protocol builders, and teams building passive-income dApps in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Blast RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Blast tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Blast tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Blast tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Blast tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Blast tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Blast dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/blast/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/blast/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Blast RPC Method

Signs a transaction with the private key of the specified account on Blast without submitting it to the network.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for DeFi developers, yield protocol builders, and teams building passive-income dApps in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Blast RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Blast tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Blast tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Blast tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Blast:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/blast/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/blast/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/blast/eth_accounts) - List accounts available for signing

---

## eth_syncing - Blast RPC Method

# eth_syncing - Blast RPC Method

Returns the sync status of your Blast node - either `false` when fully synced, or an object describing the sync progress.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_syncing` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Blast node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Blast node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Blast node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/blast/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/blast/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/blast/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Blast RPC Method

Removes a filter on Blast that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`eth_uninstallFilter` is important for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Blast RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/blast/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/blast/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/blast/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/blast/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/blast/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Blast RPC Method

Checks whether the connected Blast client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`net_listening` is useful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Blast node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Blast node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Blast RPC Method
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Blast node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/blast/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/blast/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Get node client info

---

## net_peerCount - Blast RPC Method

Returns the number of peers currently connected to your Blast node.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`net_peerCount` is important for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Blast deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Blast peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Blast peers: {peer_count}')

# net_peerCount - Blast RPC Method
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Blast peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Blast nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/blast/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/blast/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Get node client info

---

## net_version - Blast RPC Method

Returns the current network ID on Blast as a decimal string. The network ID identifies which network the node is connected to.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`net_version` is essential for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Endpoint Identification** - Confirm your application is connected to the expected Blast network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Blast network ID:', networkId);

// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Blast network ID: {network_id}')

# net_version - Blast RPC Method
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Blast network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/blast/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/blast/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/blast/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check node sync progress

---

## rpc_modules - Blast RPC Method

# rpc_modules - Blast RPC Method

Returns the enabled JSON-RPC namespaces exposed by the connected Blast endpoint together with their version strings.

> **Non-standard method.** `rpc_modules` is a client-introspection RPC that is commonly available on Geth-compatible stacks, but it is not part of the core Ethereum Execution API method set. Availability varies by client and operator policy.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`rpc_modules` is useful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Capability Discovery** - Detect whether namespaces like `debug`, `trace`, `txpool`, or `erigon` are exposed before attempting those calls
- **Client Diagnostics** - Verify what the serving node has enabled when debugging environment-specific issues
- **Infrastructure Audits** - Compare public and private endpoints to confirm which RPC surfaces are intentionally exposed
- **Runtime Feature Gating** - Adjust tooling behavior dynamically based on the actual namespaces available on a node

## Best Practices

- Call at startup to determine which features are available on a node
- Module availability varies by node client and provider configuration
- Use to gate feature access in applications before attempting unsupported calls
- This is a non-standard method; some endpoints may not expose it

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_modules",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Object, required`): Object whose keys are enabled namespaces and whose values are version strings

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "eth": "1.0",
    "net": "1.0",
    "web3": "1.0",
    "rpc": "1.0",
    "debug": "1.0",
    "trace": "1.0",
    "txpool": "1.0"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_modules",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const modules = await provider.send('rpc_modules', []);
console.log('Namespaces:', Object.keys(modules));

if (modules.debug) {
  console.log('Debug RPC is enabled');
}
```

```python
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'rpc_modules',
        'params': [],
        'id': 1,
    },
)

modules = response.json()['result']
print('Namespaces:', sorted(modules.keys()))
print('Has trace:', 'trace' in modules)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "sort"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var modules map[string]string
    err = client.CallContext(context.Background(), &modules, "rpc_modules")
    if err != nil {
        log.Fatal(err)
    }

    names := make([]string, 0, len(modules))
    for name := range modules {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Printf("Namespaces: %v\n", names)
}
```

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Inspect the client software version string
- [`debug_traceTransaction`](https://www.dwellir.com/docs/blast/debug_traceTransaction) - Debug namespace example
- `trace_transaction` - Trace namespace example

---

## web3_clientVersion - Blast RPC Method

Returns the current client software version string for your Blast node, including the client name, version number, OS, and runtime.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

## When to Use This Method

`web3_clientVersion` is valuable for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Blast client:', clientVersion);

// Using fetch
const response = await fetch('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Blast client: {client_version}')

# web3_clientVersion - Blast RPC Method
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Blast client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/blast/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/blast/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/blast/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/blast/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Blast RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Blast.

> **Why Blast?** Build on the only Ethereum L2 with native yield—4% for ETH and 5%+ for stablecoins automatically with $2.5B+ TVL, auto-rebasing ETH and USDB, gas revenue sharing for developers, and Blur-backed ecosystem.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for DeFi developers, yield protocol builders, and teams building passive-income dApps:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Blast RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-blast-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/blast/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/blast/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/blast/web3_clientVersion) - Get node client version

---

## Boba Network - Ethereum L2 Documentation

# Boba Network - Ethereum L2 Documentation

## Why Build on Boba Network?

Boba Network is the only multichain Layer 2 that delivers off-chain data and compute, enabling smarter applications for mass adoption. Built with hybrid blockchain technology, Boba Network offers:

### **Hybrid Technology**

- **HybridCompute** - Connect on-chain smart contracts to off-chain data and APIs
- **Up to 100x cheaper** than underlying blockchains
- **Fast finality** - Lightning-fast transactions and confirmations

### **Proven Security**

- **Optimistic Rollup** - Secured by the underlying blockchain
- **Battle-tested** - Based on proven Optimism technology
- **EVM compatible** - Full compatibility with existing Ethereum tools

### **Multichain Innovation**

- **First multichain L2** - Deployed on Ethereum and BNB Chain
- **Dual-fee tokens** - Pay fees in $BOBA or native currency
- **Growing ecosystem** - Active developer community and partnerships

## Quick Start with Boba Network

Connect to Boba Network in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to Boba Network mainnet
const provider = new JsonRpcProvider(
  'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to Boba Network mainnet
const web3 = new Web3(
  'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to Boba Network:', chainId === 288);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';

// Create Boba Network client
const client = createPublicClient({
  chain: {
    id: 288,
    name: 'Boba Network',
    network: 'boba',
    nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'] },
      public: { http: ['https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'] },
    },
  },
  transport: http('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

## Network Information

| Parameter    | Value       | Details      |
| ------------ | ----------- | ------------ |
| Chain ID     | 288         | Mainnet      |
| Block Time   | \~2 seconds | Average      |
| Gas Token    | ETH         | Native token |
| RPC Standard | Ethereum    | JSON-RPC 2.0 |

## API Reference

Boba Network supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/). Access all standard methods plus hybrid compute features.

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // Boba Network: Check transaction details
  console.log('Transaction confirmed on Boba Network');
  console.log('Block number:', receipt.blockNumber);

  return receipt;
}
```

### Gas Optimization

Optimize gas costs on Boba Network:

```javascript
// Estimate gas for transaction
const gasEstimate = await provider.estimateGas(tx);

// Get current gas price
const gasPrice = await provider.getGasPrice();

// Calculate total cost
const totalCost = gasEstimate * gasPrice;
console.log('Estimated cost:', totalCost.toString(), 'wei');
```

### Event Filtering

Efficiently query contract events:

```javascript
// Query events with automatic retry and pagination
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 2000; // Boba Network recommended batch size

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);
  }

  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine multiple RPC calls for optimal performance:

```javascript
const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] }
];

const results = await provider.send(batch);
```

### 2. **Connection Pooling**

Reuse provider instances to minimize connection overhead:

```javascript
// Singleton pattern for provider
class BobaProvider {
  static instance = null;

  static getInstance() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'
      );
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data to reduce API calls:

```javascript
const cache = new Map();

async function getCachedBlockData(blockNumber) {
  const key = `block_${blockNumber}`;

  if (!cache.has(key)) {
    const block = await provider.getBlock(blockNumber);
    cache.set(key, block);
  }

  return cache.get(key);
}
```

## Troubleshooting Common Issues

### Error: "Insufficient funds for gas"

Boba Network transactions require ETH for gas fees:

```javascript
// Check balance and gas requirements
const balance = await provider.getBalance(address);
const gasEstimate = await provider.estimateGas(tx);
const gasPrice = await provider.getGasPrice();
const totalRequired = gasEstimate * gasPrice + (tx.value || 0n);

if (balance < totalRequired) {
  throw new Error(`Need ${totalRequired - balance} more ETH`);
}
```

### Error: "Transaction underpriced"

Boba Network uses EIP-1559 pricing. Always use dynamic gas pricing:

```javascript
// Get current fee data
const feeData = await provider.getFeeData();

const tx = {
  to: recipient,
  value: amount,
  maxFeePerGas: feeData.maxFeePerGas,
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
  gasLimit: 21000n
};
```

### Error: "Rate limit exceeded"

Implement exponential backoff for resilient applications:

```javascript
async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 2 ** i * 1000));
      } else {
        throw error;
      }
    }
  }
}
```

## Migration Guide

### From Ethereum Mainnet

Moving from L1 to Boba Network requires minimal changes:

```javascript
// Before (Ethereum)
const provider = new JsonRpcProvider('https://eth-rpc.example.com');

// After (Boba Network)
const provider = new JsonRpcProvider(
  'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'
);

// Smart contracts work identically
// Same tooling and libraries
// Note: Different chain ID (288)
// Note: Separate block numbers
// Lower gas costs
```

## Resources & Tools

### Official Resources

- [Boba Network Documentation](https://docs.boba.network)
- [Boba Gateway Bridge](https://gateway.boba.network)
- [Boba Block Explorer](https://bobascan.com)

### Developer Tools

- [Boba Network GitHub](https://github.com/bobanetwork)
- [Developer Resources](https://docs.boba.network)

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

***

*Start building on Boba Network with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## debug_traceBlock - Boba Network RPC Method

Traces all transactions in a block on Boba Network by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Boba Network - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlock` is valuable for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on Boba Network
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - Boba Network RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on Boba Network:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in Boba Network blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/boba-network/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/boba-network/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - Boba Network RPC Method

Traces all transactions in a block on Boba Network identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Boba Network - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByHash` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on Boba Network
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - Boba Network RPC Method
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on Boba Network:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on Boba Network:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/boba-network/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/boba-network/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/boba-network/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/boba-network/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - Boba Network RPC Method

Traces all transactions in a block on Boba Network identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

This method requires an archive node with debug APIs enabled. Standard full nodes prune the historical state needed for transaction tracing. Dwellir provides archive node access for Boba Network - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceBlockByNumber` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of Boba Network execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block #999999999 not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByNumber - Boba Network RPC Method
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"tracer": "callTracer"}],
    "id": 1
  }'

# Trace specific block with prestate tracer
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["0xF4240", {"tracer": "prestateTracer"}],
    "id": 1
  }'

# Trace with default opcode tracer (minimal output)
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByNumber",
    "params": ["latest", {"disableStorage": true, "disableStack": true}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Trace latest block with call tracer
const callTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Latest block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  const gasUsed = parseInt(trace.result.gasUsed, 16);
  const status = trace.result.error ? 'REVERTED' : 'OK';
  console.log(`  ${trace.txHash}: ${gasUsed} gas [${status}]`);

  // Print sub-calls
  if (trace.result.calls) {
    for (const call of trace.result.calls) {
      console.log(`    -> ${call.type} to ${call.to}`);
    }
  }
}

// Trace a specific historical block
const blockNum = '0xF4240'; // block 1,000,000
const historicalTraces = await provider.send('debug_traceBlockByNumber', [
  blockNum,
  { tracer: 'callTracer' }
]);
console.log(`Block 1000000 had ${historicalTraces.length} transactions`);

// Trace with prestate tracer for state analysis
const prestateTraces = await provider.send('debug_traceBlockByNumber', [
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_number(block_number, tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_number, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace latest block
traces = trace_block_by_number('latest')
print(f'Latest block: {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    has_error = 'error' in result
    print(f'  {trace["txHash"]}: {gas_used} gas {"[REVERTED]" if has_error else ""}')

# Trace specific block
traces = trace_block_by_number('0xF4240')
print(f'Block 1000000: {len(traces)} transactions')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_number = w3.eth.block_number
traces = w3.provider.make_request('debug_traceBlockByNumber', [
    hex(block_number),
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions in block {block_number}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func traceBlockByNumber(blockNumber string) ([]TraceResult, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByNumber",
        "params": []interface{}{
            blockNumber,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    if err := json.Unmarshal(data, &response); err != nil {
        return nil, err
    }

    return response.Result, nil
}

func main() {
    traces, err := traceBlockByNumber("latest")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Latest block: %d transactions\n", len(traces))
    for _, trace := range traces {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED"
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Historical Gas Consumption Analysis

Profile gas usage across a range of blocks on Boba Network:

```javascript
async function analyzeGasOverRange(provider, startBlock, endBlock) {
  const blockStats = [];

  for (let block = startBlock; block <= endBlock; block++) {
    const blockHex = '0x' + block.toString(16);
    const traces = await provider.send('debug_traceBlockByNumber', [
      blockHex,
      { tracer: 'callTracer' }
    ]);

    let totalGas = 0;
    let maxGas = 0;
    let revertCount = 0;

    for (const trace of traces) {
      const gasUsed = parseInt(trace.result.gasUsed, 16);
      totalGas += gasUsed;
      maxGas = Math.max(maxGas, gasUsed);
      if (trace.result.error) revertCount++;
    }

    blockStats.push({
      block,
      txCount: traces.length,
      totalGas,
      avgGas: traces.length > 0 ? Math.round(totalGas / traces.length) : 0,
      maxGas,
      revertCount
    });

    console.log(
      `Block ${block}: ${traces.length} txs, ${totalGas} total gas, ${revertCount} reverts`
    );
  }

  return blockStats;
}
```

### 2. Automated Block Scanner for Contract Interactions

Scan blocks for interactions with a specific contract on Boba Network:

```python
import requests

def scan_blocks_for_contract(start_block, end_block, target_contract):
    target = target_contract.lower()
    interactions = []

    for block_num in range(start_block, end_block + 1):
        block_hex = hex(block_num)
        response = requests.post('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByNumber',
            'params': [block_hex, {'tracer': 'callTracer'}],
            'id': 1
        })
        traces = response.json()['result']

        for trace in traces:
            calls = flatten_calls(trace['result'])
            for call in calls:
                if call.get('to', '').lower() == target:
                    interactions.append({
                        'block': block_num,
                        'tx_hash': trace['txHash'],
                        'call_type': call['type'],
                        'from': call['from'],
                        'input': call['input'][:10],  # function selector
                        'gas_used': int(call.get('gasUsed', '0x0'), 16)
                    })

    print(f'Found {len(interactions)} interactions with {target_contract}')
    for i in interactions:
        print(f'  Block {i["block"]}: {i["tx_hash"]} [{i["call_type"]}] selector={i["input"]}')

    return interactions

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls
```

### 3. Debugging State Transitions After Protocol Upgrades

Compare block execution before and after a hard fork or protocol upgrade:

```javascript
async function compareBlockExecution(provider, forkBlock) {
  const preFork = '0x' + (forkBlock - 1).toString(16);
  const postFork = '0x' + forkBlock.toString(16);

  const [preTraces, postTraces] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { tracer: 'callTracer' }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { tracer: 'callTracer' }
    ])
  ]);

  console.log(`Pre-fork block ${forkBlock - 1}: ${preTraces.length} txs`);
  console.log(`Post-fork block ${forkBlock}: ${postTraces.length} txs`);

  // Analyze opcode-level differences for the first transaction in each
  const [preOpcodes, postOpcodes] = await Promise.all([
    provider.send('debug_traceBlockByNumber', [
      preFork,
      { disableStorage: true, enableReturnData: true }
    ]),
    provider.send('debug_traceBlockByNumber', [
      postFork,
      { disableStorage: true, enableReturnData: true }
    ])
  ]);

  // Check for new opcodes introduced after the fork
  const preOps = new Set();
  const postOps = new Set();

  for (const trace of preOpcodes) {
    for (const log of trace.result.structLogs || []) {
      preOps.add(log.op);
    }
  }

  for (const trace of postOpcodes) {
    for (const log of trace.result.structLogs || []) {
      postOps.add(log.op);
    }
  }

  const newOps = [...postOps].filter(op => !preOps.has(op));
  if (newOps.length > 0) {
    console.log('New opcodes observed after fork:', newOps);
  }
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/boba-network/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByHash) - Trace all transactions in a block by hash
- [`debug_traceTransaction`](https://www.dwellir.com/docs/boba-network/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/boba-network/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get block details by number (without traces)

---

## debug_traceCall - Boba Network RPC Method

Traces a call on Boba Network without creating a transaction on-chain. This is a dry-run trace - it executes the call in the EVM at a specified block and returns detailed execution traces including opcodes, internal calls, and state changes, without any on-chain side effects.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

This method requires an archive node with debug APIs enabled when tracing against historical blocks. For `"latest"` or `"pending"` blocks, a full node with debug APIs may suffice. Dwellir provides archive node access for Boba Network - ensure your plan includes debug namespace support.

## When to Use This Method

`debug_traceCall` is powerful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Simulating Transactions Before Sending** - Preview the full execution trace of a transaction before committing it on-chain, catching reverts and unexpected behavior before spending gas on Boba Network
- **Debugging Contract Interactions** - Step through contract execution at the opcode level to understand complex interactions, delegate calls, and proxy patterns for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Gas Estimation With Trace Details** - Go beyond `eth_estimateGas` by seeing exactly which opcodes and internal calls consume gas, enabling targeted optimization
- **Security Analysis** - Analyze how a contract would execute a specific call, detecting reentrancy, unexpected state modifications, and access control issues

## Best Practices

- Requires archive node access when tracing against historical blocks
- Use the stateDiff tracer for storage change analysis on simulated calls
- The prestateTracer shows account state before the call executes
- The callTracer is fastest for understanding call structure

## Request Parameters

- `callObject` (`Object, required`): Transaction call object (same format as eth_call)
- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)
- `from` (`DATA, optional`): Sender address (defaults to zero address)
- `to` (`DATA, required`): Recipient / contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `maxFeePerGas` (`QUANTITY, optional`): Max fee per gas (EIP-1559)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Max priority fee per gas (EIP-1559)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Encoded function call data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `structLogs[].stack` (`Array<DATA>, required`): Stack contents (if not disabled)
- `structLogs[].storage` (`Object, required`): Storage changes (if not disabled)
- `address` (`Object, required`): State of each account touched by the call
- `address.balance` (`QUANTITY, required`): Account balance
- `address.nonce` (`QUANTITY, required`): Account nonce
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots accessed

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x5e1a",
    "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000",
    "calls": [
      {
        "type": "DELEGATECALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0xfedcba0987654321fedcba0987654321fedcba09",
        "gas": "0x2fa4060",
        "gasUsed": "0x2510",
        "input": "0x70a08231000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
      }
    ]
  }
}
```

## Error Responses

### Error Response (Reverted Call)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x1234567890abcdef1234567890abcdef12345678",
    "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
    "value": "0x0",
    "gas": "0x2faf080",
    "gasUsed": "0x831b",
    "input": "0xa9059cbb...",
    "output": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020...",
    "error": "execution reverted",
    "revertReason": "ERC20: transfer amount exceeds balance"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceCall - Boba Network RPC Method
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
        "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
      },
      "latest",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with default opcode tracer
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceCall",
    "params": [
      {
        "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
        "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
      },
      "latest",
      {"disableStorage": true, "enableReturnData": true}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Trace a simple read-only contract call
const callTrace = await provider.send('debug_traceCall', [
  {
    to: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    data: '0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
  },
  'latest',
  { tracer: 'callTracer' }
]);

console.log(`Call type: ${callTrace.type}`);
console.log(`Gas used: ${parseInt(callTrace.gasUsed, 16)}`);
console.log(`Sub-calls: ${(callTrace.calls || []).length}`);

if (callTrace.error) {
  console.log(`Error: ${callTrace.error}`);
  console.log(`Revert reason: ${callTrace.revertReason}`);
} else {
  console.log(`Output: ${callTrace.output}`);
}

// Trace with prestate tracer to see state access
const prestateTrace = await provider.send('debug_traceCall', [
  {
    to: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    data: '0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
  },
  'latest',
  { tracer: 'prestateTracer' }
]);

for (const [addr, state] of Object.entries(prestateTrace)) {
  console.log(`Account ${addr}:`);
  if (state.balance) console.log(`  Balance: ${state.balance}`);
  if (state.storage) console.log(`  Storage slots: ${Object.keys(state.storage).length}`);
}
```

```python
import requests

def trace_call(call_object, block='latest', tracer='callTracer', **kwargs):
    tracer_config = {'tracer': tracer, **kwargs}
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceCall',
            'params': [call_object, block, tracer_config],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f'RPC error: {result["error"]["message"]}')
    return result['result']

# Trace a read-only contract call
call_obj = {
    'to': '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    'data': '0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
}

trace = trace_call(call_obj)
gas_used = int(trace['gasUsed'], 16)
print(f'Call type: {trace["type"]}')
print(f'Gas used: {gas_used}')

if 'error' in trace:
    print(f'Error: {trace["error"]}')
else:
    print(f'Output: {trace["output"]}')

# Show sub-calls
for call in trace.get('calls', []):
    print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

trace = w3.provider.make_request('debug_traceCall', [
    call_obj,
    'latest',
    {'tracer': 'callTracer'}
])
print(f'Result: {trace["result"]["type"]}')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type CallTrace struct {
    Type         string      `json:"type"`
    From         string      `json:"from"`
    To           string      `json:"to"`
    Value        string      `json:"value"`
    Gas          string      `json:"gas"`
    GasUsed      string      `json:"gasUsed"`
    Input        string      `json:"input"`
    Output       string      `json:"output"`
    Error        string      `json:"error,omitempty"`
    RevertReason string      `json:"revertReason,omitempty"`
    Calls        []CallTrace `json:"calls,omitempty"`
}

func main() {
    callObj := map[string]string{
        "to":   "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
        "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    }

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceCall",
        "params": []interface{}{
            callObj,
            "latest",
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result CallTrace `json:"result"`
    }
    json.Unmarshal(data, &response)

    trace := response.Result
    gasUsed, _ := strconv.ParseInt(trace.GasUsed[2:], 16, 64)

    fmt.Printf("Type: %s\n", trace.Type)
    fmt.Printf("Gas used: %d\n", gasUsed)

    if trace.Error != "" {
        fmt.Printf("Error: %s\n", trace.Error)
        fmt.Printf("Revert reason: %s\n", trace.RevertReason)
    } else {
        fmt.Printf("Output: %s\n", trace.Output)
    }

    // Print sub-calls
    for _, call := range trace.Calls {
        subGas, _ := strconv.ParseInt(call.GasUsed[2:], 16, 64)
        fmt.Printf("  -> %s to %s (%d gas)\n", call.Type, call.To, subGas)
    }
}
```

## Common Use Cases

### 1. Pre-Flight Transaction Simulation

Test a transaction before sending it on Boba Network to catch reverts and estimate costs:

```javascript
async function simulateTransaction(provider, txParams) {
  // Use callTracer to see the full call tree
  const trace = await provider.send('debug_traceCall', [
    {
      from: txParams.from,
      to: txParams.to,
      data: txParams.data,
      value: txParams.value || '0x0',
      gas: txParams.gasLimit || '0x1e8480' // 2M gas default
    },
    'latest',
    { tracer: 'callTracer' }
  ]);

  const gasUsed = parseInt(trace.gasUsed, 16);

  if (trace.error) {
    console.error('Transaction would revert!');
    console.error(`  Error: ${trace.error}`);
    console.error(`  Reason: ${trace.revertReason || 'unknown'}`);
    console.error(`  Gas wasted: ${gasUsed}`);
    return { success: false, error: trace.error, revertReason: trace.revertReason, gasUsed };
  }

  // Analyze internal calls for unexpected behavior
  const allCalls = flattenCalls(trace);
  const delegateCalls = allCalls.filter(c => c.type === 'DELEGATECALL');
  const creates = allCalls.filter(c => c.type === 'CREATE' || c.type === 'CREATE2');

  console.log('Simulation results:');
  console.log(`  Gas used: ${gasUsed}`);
  console.log(`  Internal calls: ${allCalls.length}`);
  console.log(`  Delegate calls: ${delegateCalls.length}`);
  console.log(`  Contract creations: ${creates.length}`);

  return { success: true, gasUsed, trace };
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 2. Gas Optimization Analysis

Identify the most expensive opcodes in a contract call on Boba Network:

```javascript
async function analyzeGasHotspots(provider, callObj) {
  // Use default opcode tracer for step-by-step gas analysis
  const trace = await provider.send('debug_traceCall', [
    callObj,
    'latest',
    { disableStorage: false, enableReturnData: true }
  ]);

  const opcodeGas = {};

  for (const log of trace.structLogs) {
    if (!opcodeGas[log.op]) {
      opcodeGas[log.op] = { count: 0, totalGas: 0 };
    }
    opcodeGas[log.op].count++;
    opcodeGas[log.op].totalGas += log.gasCost;
  }

  // Sort by total gas cost
  const sorted = Object.entries(opcodeGas)
    .map(([op, stats]) => ({ op, ...stats, avgGas: Math.round(stats.totalGas / stats.count) }))
    .sort((a, b) => b.totalGas - a.totalGas);

  console.log('Gas hotspots:');
  console.log('Op'.padEnd(15), 'Count'.padStart(8), 'Total Gas'.padStart(12), 'Avg Gas'.padStart(10));
  for (const entry of sorted.slice(0, 10)) {
    console.log(
      entry.op.padEnd(15),
      String(entry.count).padStart(8),
      String(entry.totalGas).padStart(12),
      String(entry.avgGas).padStart(10)
    );
  }

  // Identify SSTORE/SLOAD hotspots (most expensive storage operations)
  const storageOps = trace.structLogs.filter(
    log => log.op === 'SSTORE' || log.op === 'SLOAD'
  );
  console.log(`\nStorage operations: ${storageOps.length} (${storageOps.filter(s => s.op === 'SSTORE').length} writes)`);

  return { opcodeGas: sorted, totalSteps: trace.structLogs.length, totalGas: trace.gas };
}
```

### 3. Security Analysis of Contract Interactions

Detect potentially dangerous patterns when calling a contract on Boba Network:

```python
import requests

def security_trace_call(call_object, block='latest'):
    response = requests.post('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceCall',
        'params': [call_object, block, {'tracer': 'callTracer'}],
        'id': 1
    })
    trace = response.json()['result']

    warnings = []
    all_calls = flatten_calls(trace)

    for call in all_calls:
        # Detect unexpected delegate calls
        if call['type'] == 'DELEGATECALL':
            warnings.append(f'DELEGATECALL to {call["to"]} - could modify caller storage')

        # Detect value transfers to unexpected addresses
        value = int(call.get('value', '0x0'), 16)
        if value > 0 and call['to'] != call_object.get('to', '').lower():
            warnings.append(
                f'Value transfer of {value} wei to unexpected address {call["to"]}'
            )

        # Detect selfdestruct (CALL with no input to EOA after value)
        if call.get('error'):
            warnings.append(f'Internal revert at {call["to"]}: {call["error"]}')

    if trace.get('error'):
        print(f'TOP-LEVEL REVERT: {trace["error"]}')
        if trace.get('revertReason'):
            print(f'  Reason: {trace["revertReason"]}')
    else:
        gas_used = int(trace['gasUsed'], 16)
        print(f'Call succeeded: {gas_used} gas used')

    if warnings:
        print(f'\nSecurity warnings ({len(warnings)}):')
        for w in warnings:
            print(f'  - {w}')
    else:
        print('No security warnings detected')

    return {'success': not trace.get('error'), 'warnings': warnings}

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

# Example: analyze a token approval
security_trace_call({
    'from': '0x1234567890abcdef1234567890abcdef12345678',
    'to': '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    'data': '0x095ea7b3000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
})
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/boba-network/eth_call) - Execute a call without trace (returns only the result, not execution details)
- [`debug_traceTransaction`](https://www.dwellir.com/docs/boba-network/debug_traceTransaction) - Trace an already-executed transaction by hash
- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas for a call (without trace details)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByHash) - Trace all transactions in a block by hash

---

## debug_traceTransaction - Boba Network RPC Method

Traces a transaction execution on Boba Network by transaction hash.

This method requires an archive node. It is not available on full nodes.

## When to Use This Method

- **Analyze transaction execution step-by-step** - Trace every opcode and internal call in a completed transaction for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Debug failed transactions** - Pinpoint the exact opcode and call depth where a transaction reverted on Boba Network
- **Examine internal call traces** - Follow the full call tree including delegate calls and contract creations
- **Gas usage profiling** - Measure gas consumption per opcode to identify optimization opportunities

## Best Practices

- Requires archive node access; not available on standard full nodes
- Traces can be very large for complex transactions with many internal calls
- Use tracer options like `onlyTopCall` or `callTracer` to limit output size
- Store traces off-chain for analysis rather than querying repeatedly

## Request Parameters

- `txHash` (`DATA, required`): 32-byte transaction hash
- `tracerConfig` (`Object, optional`): Tracer configuration

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565", {"tracer": "callTracer"}],
  "id": 1
}
```

## Response Fields

- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`string, required`): Sender address
- `to` (`string, required`): Receiver address
- `gas` (`string, required`): Gas provided for the call (hex)
- `gasUsed` (`string, required`): Gas consumed by the call (hex)
- `input` (`string, required`): Call data (hex)
- `output` (`string, required`): Return data (hex), present on success
- `value` (`string, required`): Value transferred in wei (hex)
- `error` (`string, required`): Revert reason, present on failure
- `calls` (`array, required`): Nested internal calls

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0xabc...",
    "to": "0xdef...",
    "gas": "0x13880",
    "gasUsed": "0x5208",
    "input": "0x",
    "output": "0x",
    "value": "0x0"
  }
}
```

## Tracer Options

- `{}` - Default opcode tracer (verbose)
- `{ tracer: "callTracer" }` - Call tree tracer
- `{ tracer: "prestateTracer" }` - Pre-state tracer

## Code Examples

cURL
JavaScript
Python

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceTransaction",
    "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565", {"tracer": "callTracer"}],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565';

// Call tracer - shows internal calls
const callTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'callTracer' }
]);
console.log('Type:', callTrace.type);
console.log('From:', callTrace.from);
console.log('To:', callTrace.to);
console.log('Gas used:', parseInt(callTrace.gasUsed, 16));

// Prestate tracer - shows state before execution
const prestateTrace = await provider.send('debug_traceTransaction', [
  txHash,
  { tracer: 'prestateTracer' }
]);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565'

# debug_traceTransaction - Boba Network RPC Method
trace = w3.provider.make_request('debug_traceTransaction', [
    tx_hash,
    {'tracer': 'callTracer'}
])
print(f'Trace type: {trace["result"]["type"]}')
print(f'Gas used: {int(trace["result"]["gasUsed"], 16)}')
```

## Related Methods

- [`debug_traceCall`](https://www.dwellir.com/docs/boba-network/debug_traceCall) - Trace without executing
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/boba-network/debug_traceBlockByNumber) - Trace entire block

---

## eth_accounts - Boba Network RPC Method

Returns a list of addresses owned by the client on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## Important Note

On public RPC endpoints like Dwellir, `eth_accounts` returns an empty array because the node does not hold any private keys. This method is primarily useful for:

- Local development nodes (Ganache, Hardhat, Anvil)
- Private nodes with managed accounts
- Wallet provider connections (MetaMask injects accounts)

## When to Use This Method

`eth_accounts` is relevant for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access in specific scenarios:

- **Development Testing** - Retrieve test accounts from local nodes
- **Wallet Detection** - Check if a wallet provider has connected accounts
- **Client Verification** - Confirm node account access capabilities

## Best Practices

- Most public providers disable this method for security reasons; expect an empty array return
- Use wallet libraries (ethers.js, web3.py) for account management instead of relying on node-side accounts
- An empty array return is normal on managed providers and does not indicate an error condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_accounts",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<DATA>, required`): List of 20-byte account addresses owned by the client

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_accounts",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_accounts',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Accounts:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const accounts = await provider.listAccounts();
console.log('Accounts:', accounts);
```

```python
import requests

def get_accounts():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_accounts',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

accounts = get_accounts()
print(f'Accounts: {accounts}')

# eth_accounts - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Accounts: {w3.eth.accounts}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var accounts []string
    err = client.CallContext(context.Background(), &accounts, "eth_accounts")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Accounts: %v\n", accounts)
}
```

## Common Use Cases

### 1. Development Environment Detection

Check if running against a development node with test accounts:

```javascript
async function isDevEnvironment(provider) {
  const accounts = await provider.listAccounts();
  return accounts.length > 0;
}

const isDev = await isDevEnvironment(provider);
if (isDev) {
  console.log('Development environment detected');
}
```

### 2. Wallet Connection Check

Verify wallet provider has connected accounts:

```javascript
async function checkWalletConnection() {
  if (typeof window.ethereum === 'undefined') {
    return { connected: false, reason: 'No wallet detected' };
  }

  const accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  return {
    connected: accounts.length > 0,
    accounts: accounts
  };
}
```

### 3. Fallback Account Selection

Use first available account or request connection:

```javascript
async function getActiveAccount() {
  // Check existing connections
  let accounts = await window.ethereum.request({
    method: 'eth_accounts'
  });

  // Request connection if no accounts
  if (accounts.length === 0) {
    accounts = await window.ethereum.request({
      method: 'eth_requestAccounts'
    });
  }

  return accounts[0] || null;
}
```

## Related Methods

- [`eth_requestAccounts`](https://eips.ethereum.org/EIPS/eip-1102) - Request wallet connection (browser wallets)
- [`eth_getBalance`](https://www.dwellir.com/docs/boba-network/eth_getBalance) - Get account balance
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/boba-network/eth_getTransactionCount) - Get account nonce

---

## eth_blockNumber - Boba Network RPC Method

Returns the number of the most recent block on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_blockNumber` is fundamental for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Syncing Applications** - Keep your dApp in sync with the latest Boba Network blockchain state
- **Transaction Monitoring** - Verify confirmations by comparing block numbers
- **Event Filtering** - Set the correct block range for querying logs on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Health Checks** - Monitor node connectivity and sync status

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the current block number

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5BAD55"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_blockNumber",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const blockNumber = parseInt(result, 16);
console.log('Boba Network block:', blockNumber);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const blockNumber = await provider.getBlockNumber();
console.log('Boba Network block:', blockNumber);
```

```python
import requests

def get_block_number():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_blockNumber',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

block_number = get_block_number()
print(f'Boba Network block: {block_number}')

# eth_blockNumber - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Boba Network block: {w3.eth.block_number}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockNumber, err := client.BlockNumber(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Boba Network block: %d\n", blockNumber)
}
```

## Common Use Cases

### 1. Block Confirmation Counter

Monitor transaction confirmations on Boba Network:

```javascript
async function getConfirmations(provider, txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockNumber) return 0;

  const currentBlock = await provider.getBlockNumber();
  return currentBlock - tx.blockNumber + 1;
}

// Wait for specific confirmations
async function waitForConfirmations(provider, txHash, confirmations = 6) {
  let currentConfirmations = 0;

  while (currentConfirmations < confirmations) {
    currentConfirmations = await getConfirmations(provider, txHash);
    console.log(`Confirmations: ${currentConfirmations}/${confirmations}`);
    await new Promise(r => setTimeout(r, 2000));
  }

  return true;
}
```

### 2. Event Log Filtering

Query events from recent blocks on Boba Network:

```javascript
async function getRecentEvents(provider, contract, eventName, blockRange = 100) {
  const currentBlock = await provider.getBlockNumber();
  const fromBlock = currentBlock - blockRange;

  const filter = contract.filters[eventName]();
  const events = await contract.queryFilter(filter, fromBlock, currentBlock);

  return events;
}
```

### 3. Node Health Monitoring

Check if your Boba Network node is synced:

```javascript
async function checkNodeHealth(provider) {
  try {
    const blockNumber = await provider.getBlockNumber();
    const block = await provider.getBlock(blockNumber);

    const now = Date.now() / 1000;
    const blockAge = now - block.timestamp;

    if (blockAge > 60) {
      console.warn(`Node may be behind. Last block was ${blockAge}s ago`);
      return false;
    }

    console.log(`Node healthy. Latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error('Node unreachable:', error);
    return false;
  }
}
```

## Performance Optimization

### Caching Strategy

Cache block numbers to reduce API calls:

```javascript
class BlockNumberCache {
  constructor(ttl = 2000) {
    this.cache = null;
    this.timestamp = 0;
    this.ttl = ttl;
  }

  async get(provider) {
    const now = Date.now();

    if (this.cache && (now - this.timestamp) < this.ttl) {
      return this.cache;
    }

    this.cache = await provider.getBlockNumber();
    this.timestamp = now;
    return this.cache;
  }

  invalidate() {
    this.cache = null;
    this.timestamp = 0;
  }
}

const blockCache = new BlockNumberCache();
```

### Batch Requests

Combine with other calls for efficiency:

```javascript
const batch = [
  { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 },
  { jsonrpc: '2.0', method: 'eth_gasPrice', params: [], id: 2 },
  { jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 3 }
];

const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(batch)
});

const results = await response.json();
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get full block details by number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/boba-network/eth_getBlockByHash) - Get block details by hash
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check if node is still syncing

---

## eth_call - Boba Network RPC Method

Executes a new message call immediately without creating a transaction on Boba Network. Used for reading smart contract state.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_call` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Read smart contract state** - Execute view and pure functions to query token balances, DeFi positions, and protocol data without spending gas
- **Simulate transactions** - Test contract interactions before submitting them on-chain, avoiding failed transactions and wasted gas costs
- **Multi-call aggregator queries** - Batch multiple read calls into a single request using multicall contracts, reducing API overhead on Boba Network
- **MEV and arbitrage analysis** - Simulate transaction bundles to evaluate profitable opportunities before execution on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation

## Common Use Cases

### 1. Read ERC20 Token Balance

Query an ERC20 token contract to retrieve the balance for a specific wallet address using the `balanceOf(address)` function selector encoded as calldata. The selector is derived from the first 4 bytes of keccak256("balanceOf(address)"), followed by the 32-byte zero-padded address argument.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';
const walletAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

const balanceSelector = '0x70a08231' + walletAddress.slice(2).padStart(64, '0');

async function getTokenBalance() {
  const result = await provider.call({
    to: tokenAddress,
    data: balanceSelector
  });
  console.log('Balance (raw):', BigInt(result).toString());
  return result;
}

getTokenBalance();
```

### 2. Query DeFi Protocol State

Read protocol reserves, price oracles, or user positions from DeFi contracts on Boba Network. Each protocol exposes view functions that let you inspect pool state without modifying it - ideal for building analytics dashboards and trading bots.

```javascript
import { JsonRpcProvider, Interface } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const poolAbi = [
  'function getReserves() view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestamp)'
];
const poolInterface = new Interface(poolAbi);
const poolAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

async function getPoolReserves() {
  const data = poolInterface.encodeFunctionData('getReserves');
  const result = await provider.call({ to: poolAddress, data });
  const decoded = poolInterface.decodeFunctionResult('getReserves', result);
  console.log('Reserve 0:', decoded[0].toString());
  console.log('Reserve 1:', decoded[1].toString());
  return decoded;
}

getPoolReserves();
```

### 3. Simulate a Swap Before Execution

Before committing a token swap, call the router contract's quote function to calculate the expected output. This lets you validate slippage tolerance and compare rates across DEXes without risking gas on a failed trade.

```javascript
import { JsonRpcProvider, Interface, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const routerAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

const routerAbi = [
  'function getAmountsOut(uint amountIn, address[] calldata path) view returns (uint[] amounts)'
];
const routerInterface = new Interface(routerAbi);

async function simulateSwap(amountIn, tokenIn, tokenOut) {
  const data = routerInterface.encodeFunctionData('getAmountsOut', [
    parseEther(amountIn),
    [tokenIn, tokenOut]
  ]);
  const result = await provider.call({ to: routerAddress, data });
  const decoded = routerInterface.decodeFunctionResult('getAmountsOut', result);
  console.log('Expected output:', decoded[0][1].toString());
  return decoded[0][1];
}

simulateSwap('1.0', '0xTokenA...', '0xTokenB...');
```

## Best Practices

- Use `latest` for current-state reads and `pending` for pre-confirmation simulation on Boba Network
- Encode function selectors correctly: take the first 4 bytes of the keccak256 hash of the function signature
- Handle revert errors gracefully by parsing the revert reason from the error response data
- For batch reads, use multicall contracts to combine multiple `eth_call` requests into a single RPC call
- `eth_call` does not consume gas, making it ideal for unlimited read queries on Boba Network

## Request Parameters

- `from` (`DATA, optional`): 20-byte address executing the call
- `to` (`DATA, required`): 20-byte contract address
- `gas` (`QUANTITY, optional`): Gas limit for the call
- `gasPrice` (`QUANTITY, optional`): Gas price in wei
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, required`): Encoded function call data
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
    },
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The return value of the executed contract function

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_call - Boba Network RPC Method
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
      "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "data": "0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
    }, "latest"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// ERC20 ABI for common functions
const ERC20_ABI = [
  "function balanceOf(address owner) view returns (uint256)",
  "function allowance(address owner, address spender) view returns (uint256)",
  "function totalSupply() view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

// Read ERC20 token balance
async function getTokenBalance(tokenAddress, walletAddress) {
  const contract = new Contract(tokenAddress, ERC20_ABI, provider);
  const balance = await contract.balanceOf(walletAddress);
  const decimals = await contract.decimals();
  const symbol = await contract.symbol();

  return {
    raw: balance.toString(),
    formatted: (Number(balance) / Math.pow(10, decimals)).toFixed(4),
    symbol: symbol
  };
}

// Direct eth_call
async function directCall(to, data) {
  const result = await provider.call({ to, data });
  return result;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_erc20_balance(token_address, wallet_address):
    # balanceOf(address) selector
    function_signature = "balanceOf(address)"
    function_selector = w3.keccak(text=function_signature)[:4].hex()

    # Encode address parameter
    encoded_address = wallet_address[2:].lower().zfill(64)
    data = function_selector + encoded_address

    # Make the call
    result = w3.eth.call({
        'to': token_address,
        'data': data
    })

    return int(result.hex(), 16)

balance = get_erc20_balance(
    '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
)
print(f'Balance: {balance}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    data := common.FromHex("0x70a08231000000000000000000000000DeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: data,
    }

    result, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Result: 0x%x\n", result)
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas for transaction
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Send actual transaction

---

## eth_chainId - Boba Network RPC Method

Returns the chain ID used for transaction signing on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_chainId` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **EIP-155 Transaction Signing** -- Include the chain ID in transaction signatures to prevent replay attacks across different networks
- **Multi-Chain Application Routing** -- Detect which network the RPC endpoint serves and configure application logic accordingly
- **Wallet Integration** -- Verify users are connected to the expected network before prompting transaction approval
- **Cross-Chain Security** -- Validate chain identity before bridging assets or relaying messages on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation

## Common Use Cases

### 1. Multi-Network Connection Guard

Reject connections to unexpected networks before any transaction is sent:

```javascript
import { JsonRpcProvider, BrowserProvider } from 'ethers';

async function guardNetwork(provider, allowedChainIds) {
  const network = await provider.getNetwork();
  const chainId = Number(network.chainId);
  
  if (!allowedChainIds.includes(chainId)) {
    throw new Error(
      `Wrong network: connected to chain ${chainId}, expected one of [${allowedChainIds}]`
    );
  }
  
  console.log(`Connected to chain ${chainId}`);
  return chainId;
}

// Example: only allow Ethereum mainnet (1) and Arbitrum (42161)
await guardNetwork(provider, [1, 42161]);
```

### 2. Wallet Network Switcher

Detect the current chain and prompt wallet to switch if needed:

```javascript
async function ensureCorrectChain(walletProvider, targetChainId, chainParams) {
  const currentChainId = await walletProvider.send('eth_chainId', []);
  const currentId = parseInt(currentChainId, 16);
  
  if (currentId !== targetChainId) {
    try {
      await walletProvider.send('wallet_switchEthereumChain', [
        { chainId: '0x' + targetChainId.toString(16) }
      ]);
    } catch (switchError) {
      // Chain not added to wallet -- add it
      if (switchError.code === 4902) {
        await walletProvider.send('wallet_addEthereumChain', [chainParams]);
      } else {
        throw switchError;
      }
    }
    
    console.log(`Switched to chain ${targetChainId}`);
  } else {
    console.log(`Already on chain ${targetChainId}`);
  }
  
  return targetChainId;
}
```

### 3. Chain-Aware Configuration Loader

Dynamically load chain-specific contract addresses and settings:

```python
from web3 import Web3

def load_chain_config(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))
    chain_id = w3.eth.chain_id
    
    configs = {
        1: {
            'name': 'Ethereum Mainnet',
            'uniswap_router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
            'explorer': 'https://etherscan.io',
            'native_symbol': 'ETH'
        },
        137: {
            'name': 'Polygon',
            'uniswap_router': '0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff',
            'explorer': 'https://polygonscan.com',
            'native_symbol': 'MATIC'
        },
        42161: {
            'name': 'Arbitrum One',
            'uniswap_router': '0xE592427A0AEce92De3Edee1F18E0157C05861564',
            'explorer': 'https://arbiscan.io',
            'native_symbol': 'ETH'
        }
    }
    
    if chain_id not in configs:
        raise ValueError(f'Unsupported chain ID: {chain_id}')
    
    config = configs[chain_id]
    print(f'Loaded config for {config["name"]} (chain {chain_id})')
    return {'chain_id': chain_id, **config}
```

## Best Practices

- Use `eth_chainId` (not `net_version`) for EIP-155 transaction signing -- chain ID is the canonical signing value
- Cache the chain ID at application startup -- it does not change during a session
- For browser wallet dApps, use `wallet_switchEthereumChain` and `wallet_addEthereumChain` to guide users to the correct network
- Some L2 chains share the same chain ID as their L1 -- always combine chain ID checks with additional endpoint verification
- Return value is a hex-encoded integer -- parse with `parseInt(result, 16)` before using

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Chain ID in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_chainId",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const network = await provider.getNetwork();
console.log('Chain ID:', network.chainId);

// Verify network before transaction
async function verifyNetwork(expectedChainId) {
  const network = await provider.getNetwork();
  if (network.chainId !== BigInt(expectedChainId)) {
    throw new Error(`Wrong network. Expected ${expectedChainId}, got ${network.chainId}`);
  }
  return true;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

chain_id = w3.eth.chain_id
print(f'Chain ID: {chain_id}')

# eth_chainId - Boba Network RPC Method
def verify_network(expected_chain_id):
    chain_id = w3.eth.chain_id
    if chain_id != expected_chain_id:
        raise ValueError(f'Wrong network. Expected {expected_chain_id}, got {chain_id}')
    return True
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Chain ID: %d\n", chainID)
}
```

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/boba-network/net_version) - Get network version
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check sync status

---

## eth_coinbase - Boba Network RPC Method

Checks the legacy `eth_coinbase` compatibility method on Boba Network. Public endpoints may return an address, `unimplemented`, or another unsupported-method response depending on the client behind the endpoint.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

> **Note:** Treat `eth_coinbase` as a legacy compatibility probe. On shared infrastructure, this method may return `unimplemented` or another unsupported-method error, so it is not a dependable production signal.

## When to Use This Method

`eth_coinbase` is relevant for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access when you need to:

- **Check client compatibility** - Confirm whether the connected client still exposes `eth_coinbase`
- **Audit migration assumptions** - Remove Ethereum-era assumptions that every endpoint reports an etherbase address
- **Harden integrations** - Fall back to supported identity or chain-status methods when `eth_coinbase` is unavailable

## Best Practices

- Returns the node's mining address; most providers return their own address or `0x0`
- Not a reliable way to identify validators or block producers on modern chains
- Use eth\_accounts for listing user-managed accounts
- Treat unimplemented or method-not-found responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_coinbase",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (20 bytes), required`): The reported compatibility address when the client exposes eth_coinbase

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_coinbase",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_coinbase',
    params: [],
    id: 1
  })
});

const { result, error } = await response.json();

if (error) {
  console.log('No coinbase configured:', error.message);
} else {
  console.log('Boba Network coinbase:', result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const coinbase = await provider.send('eth_coinbase', []);
  console.log('Boba Network coinbase:', coinbase);
} catch (err) {
  console.log('Coinbase not configured on this node');
}
```

```python
import requests

def get_coinbase():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_coinbase',
            'params': [],
            'id': 1
        }
    )
    data = response.json()
    if 'error' in data:
        return None
    return data['result']

coinbase = get_coinbase()
if coinbase:
    print(f'Boba Network coinbase: {coinbase}')
else:
    print('No coinbase configured on this node')

# eth_coinbase - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Boba Network coinbase: {w3.eth.coinbase}')
except Exception:
    print('Coinbase not available')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var coinbase common.Address
    err = client.CallContext(context.Background(), &coinbase, "eth_coinbase")
    if err != nil {
        fmt.Println("Coinbase not configured:", err)
        return
    }

    fmt.Printf("Boba Network coinbase: %s\n", coinbase.Hex())
}
```

## Common Use Cases

### 1. Validator Configuration Verification

Verify that a node's coinbase matches the expected reward address:

```javascript
async function verifyCoinbase(provider, expectedAddress) {
  try {
    const coinbase = await provider.send('eth_coinbase', []);

    if (coinbase.toLowerCase() === expectedAddress.toLowerCase()) {
      console.log('Coinbase address verified');
      return true;
    } else {
      console.warn(`Coinbase mismatch: expected ${expectedAddress}, got ${coinbase}`);
      return false;
    }
  } catch {
    console.error('Could not retrieve coinbase - may not be configured');
    return false;
  }
}
```

### 2. Block Producer Identification

Identify the coinbase address alongside block production details:

```javascript
async function getProducerInfo(provider) {
  const [coinbase, mining, hashrate] = await Promise.allSettled([
    provider.send('eth_coinbase', []),
    provider.send('eth_mining', []),
    provider.send('eth_hashrate', [])
  ]);

  return {
    coinbase: coinbase.status === 'fulfilled' ? coinbase.value : 'not configured',
    isMining: mining.status === 'fulfilled' ? mining.value : false,
    hashrate: hashrate.status === 'fulfilled' ? parseInt(hashrate.value, 16) : 0
  };
}
```

### 3. Multi-Node Coinbase Audit

Audit coinbase addresses across a fleet of Boba Network nodes:

```javascript
async function auditCoinbases(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      try {
        const coinbase = await provider.send('eth_coinbase', []);
        return { endpoint, coinbase, configured: true };
      } catch {
        return { endpoint, coinbase: null, configured: false };
      }
    })
  );

  const unique = new Set(results.filter(r => r.configured).map(r => r.coinbase));
  console.log(`Found ${unique.size} unique coinbase address(es) across ${endpoints.length} nodes`);

  return results;
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/boba-network/eth_mining) - Check if the node is actively mining
- [`eth_hashrate`](https://www.dwellir.com/docs/boba-network/eth_hashrate) - Get the mining hash rate
- [`eth_accounts`](https://www.dwellir.com/docs/boba-network/eth_accounts) - List all accounts managed by the node

---

## eth_estimateGas - Boba Network RPC Method

Estimates the gas necessary to execute a transaction on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_estimateGas` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Calculate gas budgets** - Determine how much gas a transaction requires before submitting, helping set accurate gas limits for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Estimate costs for complex interactions** - Predict gas requirements for multi-step contract calls, such as DeFi composability chains across multiple protocols
- **Compare gas efficiency** - Evaluate gas consumption between different contract implementations to identify the most cost-effective approach on Boba Network
- **Prevent out-of-gas failures** - Verify that the gas limit is sufficient for the intended transaction to avoid wasted gas on reverted transactions

## Common Use Cases

### 1. Estimate Gas for an ERC20 Transfer

Before sending an ERC20 transfer, estimate the gas cost to ensure you set a sufficient limit. Token transfers typically consume more gas than native ETH transfers because they execute contract logic - most ERC20 implementations use around 45,000-65,000 gas per transfer.

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const erc20Abi = [
  'function transfer(address to, uint256 amount) returns (bool)'
];
const tokenContract = new Contract('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', erc20Abi, provider);

async function estimateTransferGas(to, amount) {
  const gasEstimate = await tokenContract.transfer.estimateGas(to, amount);
  console.log('Estimated gas for transfer:', gasEstimate.toString());
  return gasEstimate;
}

const gas = await estimateTransferGas('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', '1000000000000000000');
```

### 2. Simulate Contract Deployment Cost

Estimate how much gas a contract deployment will consume before submitting the creation transaction. The deployment cost depends on the bytecode size and constructor arguments - use this to budget for contract deployments on Boba Network.

```javascript
import { JsonRpcProvider, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function estimateDeploymentGas(bytecode, abi, constructorArgs) {
  const factory = new ContractFactory(abi, bytecode, provider);
  const deployTx = await factory.getDeployTransaction(...constructorArgs);
  const gasEstimate = await provider.estimateGas(deployTx);
  console.log('Estimated deployment gas:', gasEstimate.toString());
  return gasEstimate;
}

const estimatedGas = await estimateDeploymentGas(
  '0x608060...',
  ['constructor(uint256)'],
  [42]
);
```

### 3. Build Transaction with Gas Buffer

Apply a safety buffer to the estimated gas to account for minor state changes between estimation and execution. The recommended buffer varies by chain - fast, low-congestion chains like Boba Network may need only 20%, while complex DeFi interactions benefit from 50%.

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildSafeTransaction(from, to, value, contractData) {
  const [nonce, feeData] = await Promise.all([
    provider.getTransactionCount(from),
    provider.getFeeData()
  ]);

  const tx = {
    from,
    to,
    value: parseEther(value),
    data: contractData || '0x',
    nonce,
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
  };

  const estimatedGas = await provider.estimateGas(tx);
  const bufferRatio = contractData ? 1.5 : 1.2;
  tx.gasLimit = BigInt(Math.floor(Number(estimatedGas) * bufferRatio));

  console.log('Estimated gas:', estimatedGas.toString());
  console.log('Buffered gas limit:', tx.gasLimit.toString());
  return tx;
}

const tx = await buildSafeTransaction(
  '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  '0.01'
);
```

## Best Practices

- Add a 20-50% buffer to the estimated gas value for safety: simple transfers need less buffer, complex contract calls need more
- If the estimate reverts, the transaction would also revert: fix the underlying issue rather than increasing gas
- `eth_estimateGas` runs against the current state at block `latest`: state changes between estimation and execution can alter the actual gas requirement
- For EIP-1559 chains, combine `eth_estimateGas` with `eth_feeHistory` to calculate the accurate total transaction cost in native currency
- L2 chains may return significantly different gas estimates than L1 for the same contract interaction

## Request Parameters

- `from` (`DATA, optional`): Sender address
- `to` (`DATA, optional`): Recipient address
- `gas` (`QUANTITY, optional`): Gas limit
- `gasPrice` (`QUANTITY, optional`): Gas price
- `value` (`QUANTITY, optional`): Value in wei
- `data` (`DATA, optional`): Transaction data

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [{
    "from": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "value": "0x1"
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Estimated gas amount in hexadecimal

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5208"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_estimateGas",
    "params": [{
      "from": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "to": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "value": "0x1"
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Estimate simple transfer
async function estimateTransfer(to, value) {
  const gasEstimate = await provider.estimateGas({
    to: to,
    value: parseEther(value)
  });

  console.log('Estimated gas:', gasEstimate.toString());
  return gasEstimate;
}

// Estimate contract call
async function estimateContractCall(contract, method, args) {
  const gasEstimate = await contract[method].estimateGas(...args);
  console.log('Estimated gas:', gasEstimate.toString());

  // Add 20% buffer for safety
  return gasEstimate * 120n / 100n;
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def estimate_transfer(to, value_in_ether):
    gas_estimate = w3.eth.estimate_gas({
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether')
    })

    print(f'Estimated gas: {gas_estimate}')
    return gas_estimate

def estimate_contract_call(contract, method, args):
    func = getattr(contract.functions, method)
    gas_estimate = func(*args).estimate_gas()

# eth_estimateGas - Boba Network RPC Method
    return int(gas_estimate * 1.2)

# Estimate simple transfer
gas = estimate_transfer('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', 0.1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    msg := ethereum.CallMsg{
        To:    &toAddress,
        Value: big.NewInt(1000000000000000000),
    }

    gasLimit, err := client.EstimateGas(context.Background(), msg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Estimated gas: %d\n", gasLimit)
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/boba-network/eth_gasPrice) - Get current gas price
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Send transaction

---

## eth_feeHistory - Boba Network RPC Method

Returns historical gas fee data on Boba Network, including base fees per gas and priority fee percentiles for a range of recent blocks. This data is essential for building accurate fee estimation algorithms for EIP-1559 transactions.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_feeHistory` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Calculate optimal EIP-1559 fee parameters** - Derive `maxPriorityFeePerGas` from reward percentiles and `maxFeePerGas` from base fee trends for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Analyze fee market trends** - Inspect historical base fees and gas utilization ratios to forecast fee direction and time transactions for lower costs
- **Build intelligent gas pricing strategies** - Create automated fee estimation that adapts to network congestion on Boba Network without manual intervention
- **Predict future base fees** - Use the trailing `baseFeePerGas` value (index `blockCount` in the array) to anticipate the next block's base fee using EIP-1559 elasticity math

## Common Use Cases

### 1. Calculate Optimal EIP-1559 Fee Parameters

Derive `maxPriorityFeePerGas` from reward percentile data and set `maxFeePerGas` with a safety margin above the predicted next base fee. This approach gives you fee parameters tuned to real network conditions on Boba Network.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getEIP1559Fees(blockCount = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockCount.toString(16),
    'latest',
    [50, 90]
  ]);

  const nextBaseFee = BigInt(feeHistory.baseFeePerGas[blockCount]);
  const rewards50th = feeHistory.reward.map(r => BigInt(r[0]));
  const rewards90th = feeHistory.reward.map(r => BigInt(r[1]));

  const avg50th = rewards50th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);
  const avg90th = rewards90th.reduce((s, r) => s + r, 0n) / BigInt(blockCount);

  return {
    medium: {
      maxPriorityFeePerGas: avg50th,
      maxFeePerGas: nextBaseFee * 2n + avg50th
    },
    fast: {
      maxPriorityFeePerGas: avg90th,
      maxFeePerGas: nextBaseFee * 2n + avg90th
    }
  };
}

const fees = await getEIP1559Fees();
console.log('Medium priority fee:', formatUnits(fees.medium.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Fast priority fee:', formatUnits(fees.fast.maxPriorityFeePerGas, 'gwei'), 'Gwei');
```

### 2. Build Fee Estimation UI

Display recent base fee trends and provide low/medium/high fee estimates to end users. This pattern powers gas estimation widgets in wallets and dApp interfaces.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getFeeEstimateUI(blockRange = 20) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + blockRange.toString(16),
    'latest',
    [10, 50, 90]
  ]);

  const baseFees = feeHistory.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
  const rewardAvgs = [0, 1, 2].map(idx => {
    const fees = feeHistory.reward.map(r => Number(BigInt(r[idx])) / 1e9);
    return fees.reduce((s, f) => s + f, 0) / fees.length;
  });

  const nextBaseFee = baseFees[baseFees.length - 1];

  return {
    currentBaseFee: nextBaseFee,
    baseFeeTrend: baseFees.slice(-5),
    fees: {
      low: { maxPriority: rewardAvgs[0], max: nextBaseFee + rewardAvgs[0] },
      medium: { maxPriority: rewardAvgs[1], max: nextBaseFee * 2 + rewardAvgs[1] },
      high: { maxPriority: rewardAvgs[2], max: nextBaseFee * 3 + rewardAvgs[2] }
    }
  };
}

const estimate = await getFeeEstimateUI();
console.log('Base fee:', estimate.currentBaseFee, 'Gwei');
console.log('Low:', estimate.fees.low);
console.log('Medium:', estimate.fees.medium);
console.log('High:', estimate.fees.high);
```

### 3. Implement Adaptive Gas Pricing

Build a pricing engine that automatically adjusts fee parameters based on real-time network congestion. When gas utilization ratios spike above 80%, the system shifts to higher priority fees to maintain inclusion speed.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function adaptiveFeeParams(window = 10) {
  const feeHistory = await provider.send('eth_feeHistory', [
    '0x' + window.toString(16),
    'latest',
    [25, 50, 75, 90]
  ]);

  const recentUtilization = feeHistory.gasUsedRatio.slice(-3);
  const avgUtilization = recentUtilization.reduce((s, r) => s + r, 0) / recentUtilization.length;
  const baseFeePerGas = BigInt(feeHistory.baseFeePerGas[window]);

  let priorityFeePercentile;
  let baseFeeMultiplier;

  if (avgUtilization > 0.8) {
    priorityFeePercentile = 3;
    baseFeeMultiplier = 3;
    console.log('High congestion detected, using premium fees');
  } else if (avgUtilization > 0.5) {
    priorityFeePercentile = 2;
    baseFeeMultiplier = 2;
    console.log('Moderate congestion, using standard fees');
  } else {
    priorityFeePercentile = 1;
    baseFeeMultiplier = 2;
    console.log('Low congestion, using economy fees');
  }

  const maxPriorityFeePerGas = feeHistory.reward.reduce(
    (sum, r) => sum + BigInt(r[priorityFeePercentile]), 0n
  ) / BigInt(window);

  return {
    maxPriorityFeePerGas,
    maxFeePerGas: baseFeePerGas * BigInt(baseFeeMultiplier) + maxPriorityFeePerGas,
    congestionLevel: avgUtilization > 0.8 ? 'high' : avgUtilization > 0.5 ? 'moderate' : 'low'
  };
}

const params = await adaptiveFeeParams();
console.log('Adaptive fee params:', params);
```

## Best Practices

- Use 5-20 blocks of history for accurate fee estimation: fewer blocks miss recent trends, more blocks dilute signal with stale data
- Calculate `maxPriorityFeePerGas` from reward percentiles: use the 50th percentile for normal confirmation and the 90th for fast inclusion
- Multiply `baseFeePerGas` by 2x as a safety margin for `maxFeePerGas`: this covers a 12.5% base fee increase per full block over 6 consecutive blocks
- Cache fee history results with a short TTL of 12 seconds (roughly one block on Boba Network) to balance freshness with API call efficiency
- Fall back to `eth_gasPrice` if `eth_feeHistory` returns a "method not found" error, indicating the node does not support EIP-1559

## Request Parameters

- `blockCount` (`QUANTITY, required`): Number of blocks in the requested range (1 to 1024)
- `newestBlock` (`QUANTITY|TAG, required`): Highest block number or tag (latest, pending) for the range
- `rewardPercentiles` (`Array<Float>, required`): Ascending list of percentile values (0-100) to sample effective priority fees at each block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": ["0x5", "latest", [25, 50, 75]],
  "id": 1
}
```

## Response Fields

- `oldestBlock` (`QUANTITY, required`): Block number of the oldest block in the range
- `baseFeePerGas` (`Array<QUANTITY>, required`): Array of base fees per gas for each block (length = blockCount + 1, includes the next block's predicted base fee)
- `gasUsedRatio` (`Array<Float>, required`): Array of gas used ratios (0.0 to 1.0) for each block: values above 0.5 indicate blocks over 50% full
- `reward` (`Array<Array<QUANTITY>>, required`): Array of effective priority fee arrays per block, one entry per requested percentile

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "oldestBlock": "0x1076E5A",
    "baseFeePerGas": [
      "0x2E90EDD00",
      "0x2DA282B80",
      "0x2E90EDD00",
      "0x2F694E140",
      "0x2E90EDD00",
      "0x2DA282B80"
    ],
    "gasUsedRatio": [
      0.4523,
      0.5891,
      0.5234,
      0.4102,
      0.6789
    ],
    "reward": [
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x9502F900"],
      ["0x59682F00", "0x77359400", "0xB2D05E00"],
      ["0x3B9ACA00", "0x59682F00", "0x77359400"],
      ["0x77359400", "0x9502F900", "0xE8D4A51000"]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: block count must be between 1 and 1024"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_feeHistory",
    "params": ["0x5", "latest", [25, 50, 75]],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_feeHistory',
    params: ['0xa', 'latest', [25, 50, 75]],
    id: 1
  })
});

const { result } = await response.json();
const baseFees = result.baseFeePerGas.map(f => Number(BigInt(f)) / 1e9);
console.log('Base fees (Gwei):', baseFees);
console.log('Gas used ratios:', result.gasUsedRatio);

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeHistory = await provider.send('eth_feeHistory', ['0xa', 'latest', [25, 50, 75]]);

console.log('Base fees:', feeHistory.baseFeePerGas.map(f => formatUnits(f, 'gwei')));
console.log('Reward (25th):', feeHistory.reward.map(r => formatUnits(r[0], 'gwei')));
console.log('Reward (50th):', feeHistory.reward.map(r => formatUnits(r[1], 'gwei')));
console.log('Reward (75th):', feeHistory.reward.map(r => formatUnits(r[2], 'gwei')));
```

```python
import requests

def get_fee_history(block_count=10, newest_block='latest', percentiles=[25, 50, 75]):
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_feeHistory',
            'params': [hex(block_count), newest_block, percentiles],
            'id': 1
        }
    )
    return response.json()['result']

history = get_fee_history()
base_fees = [int(f, 16) / 1e9 for f in history['baseFeePerGas']]
print(f'Base fees (Gwei): {base_fees}')
print(f'Gas used ratios: {history["gasUsedRatio"]}')

# eth_feeHistory - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
fee_history = w3.eth.fee_history(10, 'latest', [25, 50, 75])
print(f'Base fees: {[w3.from_wei(f, "gwei") for f in fee_history["baseFeePerGas"]]}')
print(f'Gas used ratios: {fee_history["gasUsedRatio"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    feeHistory, err := client.FeeHistory(
        context.Background(),
        10,
        nil,
        []float64{25, 50, 75},
    )
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).SetFloat64(1e9)
    for i, baseFee := range feeHistory.BaseFee {
        fee := new(big.Float).Quo(new(big.Float).SetInt(baseFee), gwei)
        fmt.Printf("Block %d base fee: %s Gwei\n", i, fee.Text('f', 4))
    }
}
```

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/boba-network/eth_gasPrice) - Get the current legacy gas price
- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/boba-network/eth_maxPriorityFeePerGas) - Get the suggested priority fee for EIP-1559 transactions
- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_gasPrice - Boba Network RPC Method

Returns the current gas price on Boba Network in wei.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_gasPrice` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Set gas price for legacy transactions** - Use the returned wei value as the `gasPrice` field in legacy (pre-EIP-1559) transactions on Boba Network
- **Monitor network congestion** - Track gas price fluctuations to determine the best time to submit transactions for lower fees
- **Estimate transaction costs** - Multiply gas price by estimated gas units to calculate the total cost before sending any transaction
- **Build gas price oracles** - Feed real-time gas price data into fee estimation UIs, automated trading bots, and wallet applications

## Common Use Cases

### 1. Calculate Total Transaction Cost

Multiply the current gas price by the estimated gas for a transaction to determine the total cost in the native currency of Boba Network. This lets users see the expected fee before confirming any transaction.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function calculateTransactionCost(gasLimit) {
  const gasPrice = await provider.send('eth_gasPrice', []);
  const costWei = BigInt(gasPrice) * BigInt(gasLimit);
  const costEth = formatUnits(costWei, 'ether');
  console.log(`Gas price: ${formatUnits(gasPrice, 'gwei')} Gwei`);
  console.log(`Estimated cost: ${costEth} ETH`);
  return costEth;
}

await calculateTransactionCost(21000);
```

### 2. Build Dynamic Gas Price Strategy

Adjust gas prices dynamically based on current network conditions. During peak congestion on Boba Network, multiply the base gas price by 1.2-1.5x for faster inclusion; during quiet periods, use the raw gas price for the most economical confirmation.

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getDynamicGasPrice(strategy = 'medium') {
  const baseGasPrice = BigInt(await provider.send('eth_gasPrice', []));
  const multipliers = { low: 1.0, medium: 1.2, high: 1.5 };

  const adjustedPrice = baseGasPrice * BigInt(Math.floor(multipliers[strategy] * 100)) / 100n;

  console.log(`Base gas price: ${formatUnits(baseGasPrice, 'gwei')} Gwei`);
  console.log(`Strategy (${strategy}): ${formatUnits(adjustedPrice, 'gwei')} Gwei`);
  return adjustedPrice;
}

await getDynamicGasPrice('medium');
```

### 3. Compare Gas Prices Across Chains

If your application supports multiple chains, compare gas prices to route transactions to the most cost-effective network. This is particularly useful for cross-chain bridges and multi-chain DeFi aggregators.

```javascript
const chains = {
  ethereum: 'https://eth.dwellir.com',
  polygon: 'https://polygon.dwellir.com',
  arbitrum: 'https://arb.dwellir.com'
};

async function compareGasPrices() {
  const results = {};
  for (const [name, rpcUrl] of Object.entries(chains)) {
    const provider = new JsonRpcProvider(rpcUrl);
    const gasPrice = await provider.send('eth_gasPrice', []);
    results[name] = {
      wei: BigInt(gasPrice).toString(),
      gwei: Number(BigInt(gasPrice)) / 1e9
    };
  }

  const sorted = Object.entries(results).sort((a, b) => a[1].gwei - b[1].gwei);
  console.log('Cheapest chain:', sorted[0][0], '-', sorted[0][1].gwei, 'Gwei');
  return results;
}

compareGasPrices();
```

## Best Practices

- Legacy gas price is a single number: for EIP-1559 transactions, prefer using `eth_feeHistory` combined with `eth_maxPriorityFeePerGas` instead
- The return value is in wei: divide by `1e9` to convert to gwei, which is the standard unit for gas price display
- Consider current network conditions on Boba Network: multiply by 1.2-1.5x for faster block inclusion during congestion
- Most modern chains use EIP-1559 exclusively: check if Boba Network supports dynamic fee transactions before relying on legacy gas price

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Current gas price in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3b9aca00"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_gasPrice",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const feeData = await provider.getFeeData();
const gasPrice = feeData.gasPrice;

console.log('Gas Price:', formatUnits(gasPrice, 'gwei'), 'Gwei');

// Calculate transaction cost
async function estimateTransactionCost(gasLimit) {
  const feeData = await provider.getFeeData();
  const cost = feeData.gasPrice * BigInt(gasLimit);
  return formatUnits(cost, 'ether');
}

const cost = await estimateTransactionCost(21000);
console.log('Transfer cost:', cost, 'ETH');
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

gas_price = w3.eth.gas_price
print(f'Gas Price: {w3.from_wei(gas_price, "gwei")} Gwei')

# eth_gasPrice - Boba Network RPC Method
def estimate_transaction_cost(gas_limit):
    gas_price = w3.eth.gas_price
    cost = gas_price * gas_limit
    return w3.from_wei(cost, 'ether')

cost = estimate_transaction_cost(21000)
print(f'Transfer cost: {cost} ETH')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    // Convert to Gwei
    gwei := new(big.Float).Quo(
        new(big.Float).SetInt(gasPrice),
        big.NewFloat(1e9),
    )

    fmt.Printf("Gas Price: %f Gwei\n", gwei)
}
```

## Related Methods

- [`eth_maxPriorityFeePerGas`](https://www.dwellir.com/docs/boba-network/eth_maxPriorityFeePerGas) - Get priority fee (EIP-1559)
- [`eth_feeHistory`](https://www.dwellir.com/docs/boba-network/eth_feeHistory) - Get historical fee data
- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas needed

---

## eth_getBalance - Boba Network RPC Method

Returns the balance of a given address on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getBalance` is fundamental for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Wallet applications**: Display user balances and enable balance-dependent operations on Boba Network
- **Transaction validation**: Verify accounts have sufficient funds before submitting transactions to Boba Network
- **DeFi monitoring**: Track collateral positions, liquidity pools, and TVL across AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Accounting and reconciliation**: Cross-reference on-chain balances against off-chain ledger entries for financial reporting

## Request Parameters

- `address` (`DATA, required`): 20-byte address to check balance for
- `blockParameter` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Integer of the current balance in wei (hexadecimal)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a055690d9db80000"
}
```

## Common Use Cases

### 1. Display Formatted Wallet Balance with Ether Conversion

Retrieve and display a human-readable balance for any address on Boba Network. Convert the wei result to ether client-side using your web3 library.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function displayBalance(address) {
  const balanceWei = await provider.getBalance(address);
  const balance = formatEther(balanceWei);
  console.log(`Balance: ${balance} Boba Network`);
  return balance;
}

displayBalance('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000');
```

### 2. Monitor Whale Wallet Activity with Polling and Threshold Alerts

Poll a high-value wallet on Boba Network at regular intervals. Trigger an alert when the balance crosses a defined threshold, useful for tracking DeFi movements or exchange hot wallet activity.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
threshold_wei = w3.to_wei(100, 'ether')

def monitor_balance():
    previous_balance = w3.eth.get_balance(address)
    while True:
        time.sleep(15)
        current_balance = w3.eth.get_balance(address)
        if abs(current_balance - previous_balance) > threshold_wei:
            print(f'Balance changed by more than 100 Boba Network')
        if current_balance > w3.to_wei(1000, 'ether'):
            print(f'Whale alert: wallet exceeds 1000 Boba Network')
        previous_balance = current_balance

monitor_balance()
```

### 3. Historical Balance Tracking Using Block Tags

Query an account's balance at a specific block height to build a historical balance timeline. This is essential for audit trails, tax reporting, and analyzing wallet behavior over time on Boba Network.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    address := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")

    // Query balance at a specific block number
    blockNumber := big.NewInt(1000000)
    historicalBalance, _ := client.BalanceAt(context.Background(), address, blockNumber)
    fmt.Printf("Historical balance at block 1,000,000: %s wei\n", historicalBalance.String())

    // Query latest balance for comparison
    currentBalance, _ := client.BalanceAt(context.Background(), address, nil)
    change := new(big.Int).Sub(currentBalance, historicalBalance)
    fmt.Printf("Balance change: %s wei\n", change.String())
}
```

## Best Practices

- **Cache balances with short TTL**: Set a 2-5 second cache duration for balance queries to reduce RPC calls while keeping data fresh enough for most UI use cases
- **Convert wei to ether client-side**: Use `formatEther` (ethers.js) or `fromWei` (web3.py) rather than relying on node-side conversion, which the JSON-RPC does not provide
- **Use `pending` tag cautiously**: Balances returned with the `pending` block tag may reflect unconfirmed state changes and differ from finalized on-chain values
- **For batch balance queries, use `eth_call` with multicall**: When querying balances for many addresses, bundle them through a multicall contract to reduce individual RPC round-trips

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [
      "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';
const balanceWei = await provider.getBalance(address);
const balance = formatEther(balanceWei);

console.log(`Balance: ${balance}`);

// Get balance at specific block
const historicalBalance = await provider.getBalance(address, 1000000);
console.log(`Historical balance: ${formatEther(historicalBalance)}`);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
balance_wei = w3.eth.get_balance(address)
balance = w3.from_wei(balance_wei, 'ether')

print(f'Balance: {balance}')

# eth_getBalance - Boba Network RPC Method
historical_balance = w3.eth.get_balance(address, block_identifier=1000000)
print(f'Historical balance: {w3.from_wei(historical_balance, "ether")}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    balance, err := client.BalanceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Convert to ether
    fbalance := new(big.Float).SetInt(balance)
    ethValue := new(big.Float).Quo(fbalance, big.NewFloat(1e18))

    fmt.Printf("Balance: %f\n", ethValue)
}
```

## Related Methods

- [`eth_getCode`](https://www.dwellir.com/docs/boba-network/eth_getCode) - Get contract bytecode
- [`eth_getTransactionCount`](https://www.dwellir.com/docs/boba-network/eth_getTransactionCount) - Get account nonce

---

## eth_getBlockByHash - Boba Network RPC Method

Returns information about a block by hash on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getBlockByHash` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Block verification using deterministic hash lookup**: Retrieve block data by its unique, immutable hash on Boba Network
- **Chain reorganization handling**: Track blocks reliably by hash during reorgs on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively
- **Cross-chain bridge finality verification**: Confirm block existence by its canonical hash for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Deterministic queries when block number may change**: Ensure consistent results for applications that need stable references regardless of chain state

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte block hash
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
    false
  ],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used
- `transactions` (`Array, required`): Transaction objects or hashes

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x1",
    "hash": "<value>",
    "parentHash": "<value>",
    "timestamp": "0x1",
    "gasUsed": "0x1",
    "transactions": []
  }
}
```

## Common Use Cases

### 1. Verify a Specific Block from a Transaction's blockHash Field

When a transaction response includes `blockHash`, use `eth_getBlockByHash` to retrieve the full parent block. This cross-references the transaction's context and confirms which block it was included in on Boba Network.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function verifyBlockFromTx(txHash) {
  const tx = await provider.getTransaction(txHash);
  if (!tx || !tx.blockHash) return null;

  const block = await provider.getBlock(tx.blockHash);
  console.log(`Transaction ${txHash} in block #${block.number}`);
  console.log(`Block hash: ${block.hash}`);
  console.log(`Block timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
  return block;
}

verifyBlockFromTx('0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565');
```

### 2. Cross-Reference Blocks During Chain Reorganization

During a chain reorganization, block numbers can shift but block hashes remain unique identifiers. Use `eth_getBlockByHash` to verify the canonical chain state and detect whether a previously observed block has been orphaned on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def verify_block_still_canonical(block_hash):
    block = w3.eth.get_block(block_hash)
    if block is None:
        print(f'Block {block_hash} has been pruned or orphaned')
        return False
    print(f'Block {block_hash} still canonical at height #{block.number}')
    return True

# eth_getBlockByHash - Boba Network RPC Method
verify_block_still_canonical('0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437')
```

### 3. Audit Block Data by Known Hash Reference

For compliance and audit workflows, store block hashes as permanent references. Re-querying `eth_getBlockByHash` with a stored hash guarantees you retrieve the exact same block data, even months later on Boba Network.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    knownHash := common.HexToHash("0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437")
    block, err := client.BlockByHash(context.Background(), knownHash)
    if err != nil || block == nil {
        log.Fatal("Block not found: may be pruned from node")
    }

    fmt.Printf("Audited block #%d\n", block.Number().Uint64())
    fmt.Printf("Hash: %s\n", block.Hash().Hex())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Best Practices

- **Hash-based lookups are more reliable during chain reorgs than number-based**: A block hash uniquely identifies one canonical block, while a block number may shift to a different block after a reorg
- **Store block hashes in your database for future verification**: Persisting the hash alongside related records enables deterministic re-querying for audits and data integrity checks
- **Handle `null` results gracefully**: Blocks can be pruned by the node, especially on non-archive endpoints; your application should treat a null response as a missing or unavailable block
- **For L2 optimistic rollups, verify the L1 anchor hash separately**: The hash on the L2 chain references a different block space than the L1 anchor; validate both independently for full finality confidence

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByHash",
    "params": [
      "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
      false
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const blockHash = '0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437';
const block = await provider.getBlock(blockHash);

console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

block_hash = '0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437'
block = w3.eth.get_block(block_hash)

print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    blockHash := common.HexToHash("0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437")
    block, err := client.BlockByHash(context.Background(), blockHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
}
```

## Related Methods

- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get block by number
- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get latest block number

---

## eth_getBlockByNumber - Boba Network RPC Method

Returns information about a block by block number on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getBlockByNumber` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Block explorers and analytics dashboards**: Display comprehensive block data and chain metrics for end-user interfaces on Boba Network
- **Transaction indexers processing block contents**: Extract and index every transaction within a block for data pipelines and search backends
- **Cross-chain bridges verifying block data**: Validate block headers and transaction proofs for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Timestamp-based logic**: Verify block age and enforce time-dependent contract logic on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number in hex, or "latest", "earliest", "pending", "safe", "finalized"
- `fullTransactions` (`Boolean, required`): If true, returns full transaction objects; if false, returns transaction hashes

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": ["latest", false],
  "id": 1
}
```

## Response Fields

- `number` (`QUANTITY, required`): Block number
- `hash` (`DATA, required`): 32-byte block hash
- `parentHash` (`DATA, required`): 32-byte parent block hash
- `timestamp` (`QUANTITY, required`): Unix timestamp
- `gasUsed` (`QUANTITY, required`): Total gas used by all transactions
- `gasLimit` (`QUANTITY, required`): Maximum gas allowed in block
- `transactions` (`Array, required`): Array of transaction objects or hashes
- `baseFeePerGas` (`QUANTITY, required`): Base fee per gas (EIP-1559)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "number": "0x5BAD55",
    "hash": "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
    "parentHash": "0x...",
    "timestamp": "0x64d8f6d0",
    "gasUsed": "0x1234",
    "gasLimit": "0x1c9c380",
    "transactions": [],
    "baseFeePerGas": "0x5f5e100"
  }
}
```

## Common Use Cases

### 1. Process All Transactions in the Latest Block

Fetch the latest block on Boba Network with full transaction objects, then iterate through each transaction to extract sender, receiver, value, and gas data. This pattern powers indexers, analytics dashboards, and event backfill pipelines.

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function processLatestBlock() {
  const block = await provider.getBlock('latest', true);

  if (!block) return;

  console.log(`Block #${block.number}: ${block.transactions.length} transactions`);

  for (const tx of block.prefetchedTransactions) {
    console.log(`  ${tx.hash}`);
    console.log(`    From: ${tx.from}  To: ${tx.to}`);
    console.log(`    Value: ${formatEther(tx.value)}  Gas: ${tx.gasLimit.toString()}`);
  }
}

processLatestBlock();
```

### 2. Monitor New Blocks with a Polling Loop

Poll `eth_getBlockByNumber` at regular intervals to detect new blocks as they are produced on Boba Network. This lightweight pattern is suitable for bots, watchers, and notification services that need near-real-time block awareness.

```python
from web3 import Web3
import time

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

last_block = w3.eth.block_number

print(f'Starting from block #{last_block}')

while True:
    current_block = w3.eth.block_number
    if current_block > last_block:
        for block_num in range(last_block + 1, current_block + 1):
            block = w3.eth.get_block(block_num)
            tx_count = len(block.transactions)
            print(f'New block #{block.number}: {tx_count} txns, '
                  f'gas used {block.gasUsed}')
        last_block = current_block
    time.sleep(2)
```

### 3. Verify Block Finality on L2 Chains

Layer 2 rollup chains on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively expose additional block tags that indicate settlement confidence. Use `safe` or `finalized` tags alongside the base `latest` tag for use cases requiring strong finality guarantees.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    // Latest block (may be unconfirmed on L2)
    latest, _ := client.BlockByNumber(context.Background(), nil)
    fmt.Printf("Latest block: %d (timestamp: %d)\n",
        latest.Number().Uint64(), latest.Time())

    // Finalized block (settled on L1 for rollups)
    finalized, _ := client.BlockByNumber(context.Background(),
        big.NewInt(int64(RPCLatestBlockNumber-32))) // placeholder: use rpc.FinalizedBlockNumber
    if finalized != nil {
        fmt.Printf("Finalized block: %d\n", finalized.Number().Uint64())
    }
}
```

## Best Practices

- **Cache block data by block number**: Blocks are immutable once finalized, so cache results indefinitely keyed by block number to eliminate redundant API calls
- **Use `latest` tag for most use cases; avoid `pending` for production**: The `pending` tag returns speculative data that may never be included in the canonical chain
- **For L2 chains, check chain-specific finality tags**: Tags like `safe` and `finalized` provide settlement guarantees specific to each rollup's proof mechanism
- **Combine with `eth_getBlockReceipts` for efficient receipt scanning**: When you need both block headers and transaction outcomes, use `eth_getBlockReceipts` alongside this method rather than fetching receipts individually

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockByNumber",
    "params": ["latest", false],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get latest block
const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Timestamp:', new Date(block.timestamp * 1000));
console.log('Transactions:', block.transactions.length);

// Get block with full transactions
const blockWithTxs = await provider.getBlock('latest', true);
for (const tx of blockWithTxs.prefetchedTransactions) {
  console.log('Transaction:', tx.hash);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_getBlockByNumber - Boba Network RPC Method
block = w3.eth.get_block('latest')
print(f'Block number: {block.number}')
print(f'Timestamp: {block.timestamp}')
print(f'Transactions: {len(block.transactions)}')

# Get block with full transactions
block_full = w3.eth.get_block('latest', full_transactions=True)
for tx in block_full.transactions:
    print(f'Transaction: {tx.hash.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Get latest block
    block, err := client.BlockByNumber(context.Background(), nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Block number: %d\n", block.Number().Uint64())
    fmt.Printf("Timestamp: %d\n", block.Time())
    fmt.Printf("Transactions: %d\n", len(block.Transactions()))
}
```

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get latest block number
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/boba-network/eth_getBlockByHash) - Get block by hash
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/boba-network/eth_getTransactionByHash) - Get transaction details

---

## eth_getBlockReceipts - Boba Network RPC Method

# eth_getBlockReceipts - Boba Network RPC Method

Returns all transaction receipts for a block on Boba Network. This is more efficient than calling `eth_getTransactionReceipt` once per transaction when you already know the target block.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getBlockReceipts` is useful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Indexer Backfills**: Pull every receipt in a block with one request instead of looping over transaction hashes
- **Event Collection**: Scan all logs emitted by a block when building analytics or data pipelines
- **Settlement Auditing**: Verify every transaction outcome in a target block for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Operational Debugging**: Compare receipt-level gas usage, status, and logs across multiple transactions at once

## Request Parameters

- `block` (`QUANTITY | TAG | DATA, required`): Block number, block tag such as latest, or 32-byte block hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": ["0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437"],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object> | null, required`): Array of receipt objects for the block, or null if the block is not found
- `transactionHash` (`DATA, required`): Transaction hash
- `status` (`QUANTITY, required`): 0x1 on success, 0x0 on failure
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in the block up to this transaction
- `logs` (`Array<Object>, required`): Logs emitted by the transaction
- `contractAddress` (`DATA | null, required`): Created contract address for deployment transactions
- `effectiveGasPrice` (`QUANTITY, required`): Effective gas price paid by the sender

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "transactionHash": "0x50b1857dce8dbe401e09610534de81655bc508c6765eb30ecefe24148c515c28",
      "transactionIndex": "0x0",
      "blockHash": "0x0ee8240b9393d92d059f1a87f4845ca5fd75f70aca8b1a97d98e1ea2560edf34",
      "blockNumber": "0xab47b2",
      "from": "0x0bd34b0a5be345c9bf7a147eb698e993511180cb",
      "to": "0xccc88a9d1b4ed6b0eaba998850414b24f1c315be",
      "gasUsed": "0x10b58",
      "cumulativeGasUsed": "0x10b58",
      "effectiveGasPrice": "0x9c7652400",
      "status": "0x0",
      "logs": [
        {
          "address": "0x20c0000000000000000000000000000000000000",
          "topics": [
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
          ],
          "data": "0x0000000000000000000000000000000000000000000000000000000000000b3b",
          "logIndex": "0x0",
          "removed": false
        }
      ]
    }
  ]
}
```

## Common Use Cases

### 1. Backfill Transaction Receipts for an Indexer

When bootstrapping an indexer for Boba Network, use `eth_getBlockReceipts` to backfill historical receipt data efficiently. One RPC call per block replaces dozens of individual `eth_getTransactionReceipt` calls.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function backfillReceipts(startBlock, endBlock) {
  const receipts = {};

  for (let i = startBlock; i <= endBlock; i++) {
    const hexBlock = '0x' + i.toString(16);
    const results = await provider.send('eth_getBlockReceipts', [hexBlock]);

    if (results) {
      for (const receipt of results) {
        receipts[receipt.transactionHash] = {
          block: i,
          status: receipt.status === '0x1' ? 'success' : 'failed',
          gasUsed: parseInt(receipt.gasUsed, 16),
          logCount: receipt.logs.length,
        };
      }
    }
    console.log(`Backfilled block ${i}: ${results ? results.length : 0} receipts`);
  }

  return receipts;
}

backfillReceipts(10000000, 10000050);
```

### 2. Audit Gas Usage Across All Transactions in a Range

Compute total gas consumption and identify high-gas transactions within a target block range on Boba Network. This is useful for gas cost analysis and identifying optimization targets in smart contract usage.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def audit_gas_usage(block_identifier):
    response = w3.provider.make_request(
        'eth_getBlockReceipts', [block_identifier]
    )

    receipts = response.get('result')
    if not receipts:
        print(f'No receipts found for block {block_identifier}')
        return

    total_gas = 0
    for receipt in receipts:
        gas = int(receipt['gasUsed'], 16)
        total_gas += gas
        if gas > 500_000:
            print(f'High gas tx: {receipt["transactionHash"]} used {gas:,} gas')

    print(f'Block {block_identifier}: {len(receipts)} txs, '
          f'total gas {total_gas:,}')

audit_gas_usage('0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437')
```

### 3. Extract Contract Creation Events from Deployment Blocks

When monitoring contract deployments on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively, use `eth_getBlockReceipts` to scan for receipts where `contractAddress` is non-null. This identifies all new contract deployments within a block in a single call.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, _ := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    var receipts []map[string]interface{}
    err := client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
    )
    if err != nil {
        log.Fatal(err)
    }

    for _, r := range receipts {
        contractAddr, ok := r["contractAddress"]
        if ok && contractAddr != nil {
            fmt.Printf("Contract deployed: %s\n", contractAddr)
            fmt.Printf("  Creator: %s\n", r["from"])
            fmt.Printf("  Tx hash: %s\n", r["transactionHash"])
        }
    }
}
```

## Best Practices

- **Use block hash instead of block number for deterministic results**: Hash-based lookups guarantee you are querying the exact block intended, even if chain reorganizations shift block numbers
- **Paginate large receipt arrays client-side**: Blocks with thousands of transactions return large payloads; paginate processing to avoid memory pressure in your application
- **Cache individual receipt data per transaction hash**: Receipts are immutable once a block is finalized, so cache them indefinitely for repeated lookups
- **For historical blocks, archive nodes may return more complete receipt data**: Full nodes may prune older state; archive nodes retain complete historical receipt information

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBlockReceipts",
    "params": ["0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const receipts = await provider.send('eth_getBlockReceipts', [
  '0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437',
]);

console.log('Receipt count:', receipts.length);
console.log('First tx status:', receipts[0]?.status);
console.log('First tx logs:', receipts[0]?.logs.length ?? 0);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

response = w3.provider.make_request(
    'eth_getBlockReceipts',
    ['0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437'],
)

receipts = response['result']
print(f'Receipt count: {len(receipts)}')
print(f'First tx status: {receipts[0][\"status\"]}')
print(f'First tx logs: {len(receipts[0][\"logs\"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var receipts []map[string]any
    err = client.CallContext(
        context.Background(),
        &receipts,
        "eth_getBlockReceipts",
        "0x164c0bb90ae67c0b6c3ab90a32a8cd3917f1d3840187fb874f00f7e417dd9437",
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Receipt count: %d\n", len(receipts))
    if len(receipts) > 0 {
        fmt.Printf("First tx status: %v\n", receipts[0]["status"])
    }
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/boba-network/eth_getTransactionReceipt) - Retrieve a single transaction receipt
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/boba-network/eth_getBlockByHash) - Retrieve the block object itself
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Retrieve a block by number or tag

---

## eth_getCode - Boba Network RPC Method

Returns the bytecode at a given address on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getCode` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Contract Verification** -- Verify that the bytecode deployed at an address matches the expected source code compilation output on Boba Network
- **EOA vs Contract Detection** -- Determine whether an address is an externally owned account (returns `0x`) or a deployed smart contract (returns bytecode)
- **Proxy Pattern Detection** -- Check if a proxy contract has been initialized by examining whether its implementation slot contains code
- **Security Auditing** -- Validate contract deployments before interacting with them on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation

## Common Use Cases

### 1. Detect Contract vs EOA

Determine if an address is a smart contract or a regular wallet on Boba Network:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x' && code !== '0x0';
}

async function classifyAddress(address) {
  if (await isContract(address)) {
    const balance = await provider.getBalance(address);
    console.log('Contract at', address, '- balance:', balance.toString());
    return 'contract';
  }
  console.log('EOA at', address);
  return 'eoa';
}
```

### 2. Verify Proxy Implementation Initialization

Check whether a proxy contract has been initialized with an implementation on Boba Network:

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function checkProxyInitialized(proxyAddress) {
  // EIP-1967 implementation slot
  const IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
  
  const slotValue = await provider.getStorage(proxyAddress, IMPL_SLOT);
  const implAddress = '0x' + slotValue.slice(26);
  
  const implCode = await provider.getCode(implAddress);
  const hasCode = implCode !== '0x' && implCode !== '0x0';
  
  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress} (has code: ${hasCode})`);
  
  return hasCode;
}
```

### 3. Batch Contract Detection

Scan multiple addresses to classify them efficiently:

```javascript
async function scanAddresses(provider, addresses) {
  const results = [];
  
  for (const address of addresses) {
    try {
      const code = await provider.getCode(address);
      const type = (code === '0x' || code === '0x0') ? 'EOA' : 'Contract';
      results.push({ address, type, bytecodeSize: code.length });
    } catch (error) {
      results.push({ address, type: 'Error', error: error.message });
    }
  }
  
  const summary = {
    total: results.length,
    contracts: results.filter(r => r.type === 'Contract').length,
    eoas: results.filter(r => r.type === 'EOA').length,
    errors: results.filter(r => r.type === 'Error').length
  };
  
  console.log('Scan results:', summary);
  return { results, summary };
}
```

## Best Practices

- Check both `0x` and `0x0` return values -- different client implementations return one or the other for EOAs
- Use historical block numbers with `eth_getCode` to verify contract state at a specific point in time
- For proxy pattern detection, combine `eth_getCode` with `eth_getStorageAt` to fully verify initialization
- Cache bytecode by address, as deployed contract code is immutable
- The return value length is roughly 2x the deployment bytecode size (hex encoding doubles the byte count)

## Request Parameters

- `address` (`DATA, required`): 20-byte address
- `blockParameter` (`QUANTITY|TAG, required`): Block number or tag

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): Contract bytecode or 0x if EOA

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "<value>"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getCode",
    "params": [
      "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';
const code = await provider.getCode(address);

if (code === '0x') {
  console.log('Address is an EOA (externally owned account)');
} else {
  console.log('Address is a contract');
  console.log('Bytecode length:', code.length);
}

// Check if address is a contract
async function isContract(address) {
  const code = await provider.getCode(address);
  return code !== '0x';
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
code = w3.eth.get_code(address)

if code == b'':
    print('Address is an EOA')
else:
    print('Address is a contract')
    print(f'Bytecode length: {len(code.hex())}')

# eth_getCode - Boba Network RPC Method
def is_contract(address):
    code = w3.eth.get_code(address)
    return code != b''
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    code, err := client.CodeAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }

    if len(code) == 0 {
        fmt.Println("Address is an EOA")
    } else {
        fmt.Printf("Contract bytecode length: %d\n", len(code))
    }
}
```

## Related Methods

- [`eth_getBalance`](https://www.dwellir.com/docs/boba-network/eth_getBalance) - Get account balance
- [`eth_getStorageAt`](https://www.dwellir.com/docs/boba-network/eth_getStorageAt) - Get contract storage

---

## eth_getFilterChanges - Boba Network RPC Method

Polls a filter on Boba Network and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created - log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getFilterChanges` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Event Streaming** - Incrementally consume new contract events without re-fetching the entire log history on Boba Network
- **Real-Time Monitoring** - Track contract activity, token transfers, or governance votes for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Efficient Log Indexing** - Process only new events since your last poll, minimizing bandwidth and compute overhead
- **Block & Transaction Tracking** - When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

## Best Practices

- Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
- Always call `eth_uninstallFilter` when monitoring is complete to release server-side resources
- Handle null or empty array returns gracefully as they indicate no new results since the last poll
- Use WebSocket subscriptions (`eth_subscribe`) instead of polling for real-time production applications

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes of new blocks
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes of pending transactions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456...",
      "logIndex": "0x0",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a log filter first
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Poll for new events
async function pollFilter(filterId, interval = 2000) {
  while (true) {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      for (const log of changes) {
        console.log('New event in block:', parseInt(log.blockNumber, 16));
        console.log('  Contract:', log.address);
        console.log('  Data:', log.data);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollFilter(filterId);
```

```python
import requests
import time

RPC_URL = 'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# eth_getFilterChanges - Boba Network RPC Method
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
}])
filter_id = filter_result['result']

# Poll for changes
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    logs = changes.get('result', [])
    if logs:
        for log in logs:
            block = int(log['blockNumber'], 16)
            print(f'New event in block {block}: {log["address"]}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a log filter
    contractAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
    }

    // Using SubscribeFilterLogs for real-time events
    logs := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logs)
    if err != nil {
        // Fallback: use polling with FilterLogs
        ticker := time.NewTicker(2 * time.Second)
        currentBlock, _ := client.BlockNumber(context.Background())

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                results, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range results {
                        fmt.Printf("Log in block %d from %s\n", l.BlockNumber, l.Address.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logs:
            fmt.Printf("Log in block %d from %s\n", vLog.BlockNumber, vLog.Address.Hex())
        }
    }
}
```

## Common Use Cases

### 1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Boba Network:

```javascript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring transfers on ${tokenAddress}...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);
        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}
```

### 2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

```javascript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}
```

### 3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the `removed` flag:

```javascript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

  setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization - undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/boba-network/eth_newFilter) - Create a log/event filter to poll with this method
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/boba-network/eth_newBlockFilter) - Create a block filter for new block notifications
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/boba-network/eth_getFilterLogs) - Get all logs matching a filter (full history, not incremental)
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/boba-network/eth_uninstallFilter) - Remove a filter when no longer needed
- [`eth_getLogs`](https://www.dwellir.com/docs/boba-network/eth_getLogs) - Query logs directly without creating a filter

---

## eth_getFilterLogs - Boba Network RPC Method

Returns an array of all logs matching the filter that was previously created with `eth_newFilter` on Boba Network. Unlike `eth_getFilterChanges` which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getFilterLogs` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Initial Log Retrieval** - Fetch the complete set of matching logs when you first create a filter on Boba Network
- **Backfilling Event Data** - Recover historical events after an indexer restart or gap in polling
- **One-Time Queries** - Retrieve all logs for a specific block range without incremental polling
- **Data Reconciliation** - Compare against incrementally collected data from `eth_getFilterChanges` to detect missed events

## Best Practices

- Prefer `eth_getLogs` for most use cases; this method is designed for filters created with `eth_newFilter`
- Results are subject to node log retention limits; historical queries may return incomplete data
- Always call `eth_uninstallFilter` after retrieving logs to free filter resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The filter ID returned by eth_newFilter. Only log filters are supported - block and pending transaction filters will return an error

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterLogs",
  "params": ["0x1a"],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Address from which the log originated
- `topics` (`Array<DATA>, required`): Array of 0-4 indexed log arguments (32 bytes each)
- `data` (`DATA, required`): Non-indexed log arguments (ABI-encoded)
- `blockNumber` (`QUANTITY, required`): Block number where the log was emitted
- `transactionHash` (`DATA, required`): Hash of the transaction that generated the log
- `transactionIndex` (`QUANTITY, required`): Index position of the transaction in the block
- `blockHash` (`DATA, required`): Hash of the block containing the log
- `logIndex` (`QUANTITY, required`): Log index position in the block
- `removed` (`Boolean, required`): true if the log was removed due to a chain reorganization

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x000000000000000000000000a7a93fd0a276fc1c0197a5b5623ed117786bac38",
        "0x0000000000000000000000002f3e5f5c0b5e9b0c7e1a0e8b2f6c3d4e5a6b7c8d"
      ],
      "data": "0x00000000000000000000000000000000000000000000000000000000001e8480",
      "blockNumber": "0x1234567",
      "transactionHash": "0xabc123def456789...",
      "transactionIndex": "0x0",
      "blockHash": "0xdef456abc789012...",
      "logIndex": "0x0",
      "removed": false
    },
    {
      "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
        "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
        "0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef12"
      ],
      "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
      "blockNumber": "0x1234568",
      "transactionHash": "0x789abc012def345...",
      "transactionIndex": "0x3",
      "blockHash": "0x012345def678abc...",
      "logIndex": "0x2",
      "removed": false
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_getFilterLogs - Boba Network RPC Method
FILTER_ID=$(curl -s -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for a specific block range
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: '0x1234500',
  toBlock: '0x1234600',
  address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

// Get all matching logs at once
const logs = await provider.send('eth_getFilterLogs', [filterId]);
console.log(`Found ${logs.length} matching logs`);

for (const log of logs) {
  console.log(`Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
}

// Clean up the filter
await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests

RPC_URL = 'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for a specific block range
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': '0x1234500',
    'toBlock': '0x1234600',
    'address': '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
}])
filter_id = filter_result['result']

# Get all matching logs
logs_result = rpc_call('eth_getFilterLogs', [filter_id])
logs = logs_result.get('result', [])

print(f'Found {len(logs)} matching logs')
for log in logs:
    block = int(log['blockNumber'], 16)
    print(f'  Block {block}: {log["transactionHash"]}')

# Clean up
rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0x1234500),
        ToBlock:   big.NewInt(0x1234600),
        Addresses: []common.Address{contractAddress},
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d matching logs\n", len(logs))
    for _, l := range logs {
        fmt.Printf("  Block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
    }
}
```

## Common Use Cases

### 1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

```javascript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}
```

### 2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

```javascript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}
```

### 3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

```javascript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/boba-network/eth_newFilter) - Create the log filter whose results this method returns
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll for only new logs since the last call (incremental)
- [`eth_getLogs`](https://www.dwellir.com/docs/boba-network/eth_getLogs) - Query logs directly without creating a filter first
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/boba-network/eth_uninstallFilter) - Remove a filter when no longer needed

---

## eth_getLogs - Boba Network RPC Method

# eth_getLogs - Boba Network RPC Method

Returns an array of all logs matching a given filter object on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_getLogs` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Index smart contract events** - Track transfers, swaps, and approvals emitted by any contract on Boba Network for use in indexed databases
- **Monitor DeFi protocol activity** - Watch for liquidity changes, price updates, and position events in real time across AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Build analytics pipelines** - Extract on-chain event data for dashboards, reporting, and trend analysis on Boba Network
- **Track token holder activity** - Monitor whale movements and large transfers to detect significant market activity

## Common Use Cases

### 1. Monitor ERC20 Transfer Events

Track all transfer events for a specific token contract within a defined block range. The Transfer event signature hash filters for exactly this event type, and you can optionally filter by sender or recipient address using indexed topics.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const tokenAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';
const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getRecentTransfers(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [transferTopic]
  });

  const transfers = logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount: BigInt(log.data).toString(),
    txHash: log.transactionHash
  }));

  console.log(`Found ${transfers.length} transfers`);
  return transfers;
}

const recentTransfers = await getRecentTransfers('latest', 'latest');
```

### 2. Track DEX Swap Events

Capture swap events from a DEX to analyze trading volume, price impact, and liquidity flow. Each swap emits event parameters that include the amounts and addresses involved - ideal for building price feeds and volume aggregators.

```javascript
const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
const pairAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

async function getRecentSwaps(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: pairAddress,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [SWAP_TOPIC]
  });

  return logs.map(log => ({
    sender: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    amount0In: BigInt('0x' + log.data.slice(2, 66)).toString(),
    amount1In: BigInt('0x' + log.data.slice(66, 130)).toString(),
    amount0Out: BigInt('0x' + log.data.slice(130, 194)).toString(),
    amount1Out: BigInt('0x' + log.data.slice(194, 258)).toString(),
    txHash: log.transactionHash
  }));
}

const swaps = await getRecentSwaps('latest', 'latest');
console.log(`Found ${swaps.length} swaps`);
```

### 3. Multi-Contract Event Aggregation

Query events from multiple contracts simultaneously by passing an array of addresses. This is useful for cross-protocol analytics - aggregating lending, borrowing, and liquidation events from multiple DeFi protocols in a single query on Boba Network.

```javascript
const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const contracts = [
  '0xContractA...',
  '0xContractB...',
  '0xContractC...'
];

const EVENT_TOPIC = '0x...';

async function aggregateEvents(fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: contracts,
    fromBlock: fromBlock,
    toBlock: toBlock,
    topics: [EVENT_TOPIC]
  });

  const grouped = {};
  for (const log of logs) {
    const contract = log.address;
    if (!grouped[contract]) grouped[contract] = [];
    grouped[contract].push(log);
  }

  for (const [contract, events] of Object.entries(grouped)) {
    console.log(`${contract}: ${events.length} events`);
  }

  return grouped;
}

aggregateEvents('0x100000', '0x100500');
```

## Best Practices

- Limit block range to 1,000-5,000 blocks per query to avoid timeouts and rate limits on Boba Network
- Use topic filters for efficient log filtering: the node filters at the storage level before returning results
- For high-volume monitoring, use `eth_newFilter` with polling instead of repeatedly calling `eth_getLogs`
- Store the last processed block number for incremental indexing rather than re-scanning the full chain
- Be aware that `eth_getLogs` often has stricter rate limits than other RPC methods on Boba Network

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block (default: "latest")
- `toBlock` (`QUANTITY|TAG, optional`): Ending block (default: "latest")
- `address` (`DATA|Array, optional`): Contract address(es) to filter
- `topics` (`Array, optional`): Array of topic filters
- `blockHash` (`DATA, optional`): Filter single block by hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `address` (`DATA, required`): Contract that emitted the log
- `topics` (`Array, required`): Array of indexed topics
- `data` (`DATA, required`): Non-indexed log data
- `blockNumber` (`QUANTITY, required`): Block number
- `transactionHash` (`DATA, required`): Transaction hash
- `logIndex` (`QUANTITY, required`): Log index in block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [{
    "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x...", "0x..."],
    "data": "0x...",
    "blockNumber": "0x5BAD55",
    "transactionHash": "0x...",
    "logIndex": "0x0"
  }]
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [{
      "fromBlock": "latest",
      "toBlock": "latest",
      "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Contract } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Get Transfer events
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

async function getTransferEvents(tokenAddress, fromBlock, toBlock) {
  const logs = await provider.getLogs({
    address: tokenAddress,
    topics: [TRANSFER_TOPIC],
    fromBlock: fromBlock,
    toBlock: toBlock
  });

  return logs.map(log => ({
    from: '0x' + log.topics[1].slice(26),
    to: '0x' + log.topics[2].slice(26),
    blockNumber: log.blockNumber,
    transactionHash: log.transactionHash
  }));
}

const events = await getTransferEvents(
  '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  'latest',
  'latest'
);
console.log('Transfer events:', events);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

def get_transfer_events(token_address, from_block, to_block):
    logs = w3.eth.get_logs({
        'address': token_address,
        'topics': [TRANSFER_TOPIC],
        'fromBlock': from_block,
        'toBlock': to_block
    })

    events = []
    for log in logs:
        events.append({
            'from': '0x' + log['topics'][1].hex()[26:],
            'to': '0x' + log['topics'][2].hex()[26:],
            'block': log['blockNumber'],
            'tx': log['transactionHash'].hex()
        })

    return events

events = get_transfer_events(
    '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    'latest',
    'latest'
)
print(f'Found {len(events)} transfer events')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    transferTopic := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")

    query := ethereum.FilterQuery{
        FromBlock: big.NewInt(0),
        ToBlock:   nil,
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    logs, err := client.FilterLogs(context.Background(), query)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d events\n", len(logs))
}
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/boba-network/eth_newFilter) - Create a filter for logs
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll filter for new logs

---

## eth_getStorageAt - Boba Network RPC Method

Returns the value from a storage position at a given address on Boba Network. This provides direct access to the raw EVM storage of any smart contract, bypassing ABI encoding and public getter functions.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getStorageAt` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Reading Private State** - Access contract state variables that have no public getter, including variables marked as `private` or `internal` in Solidity
- **Proxy Implementation Verification** - Read the implementation address from EIP-1967 proxy storage slots to verify which logic contract a proxy delegates to on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Storage Layout Analysis** - Inspect raw storage slots for security auditing, debugging, or reverse-engineering contract behavior
- **State Change Monitoring** - Track specific storage slot changes across blocks to monitor protocol parameters, admin roles, or balances

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address of the contract to read storage from
- `position` (`QUANTITY, required`): Hex-encoded storage slot position (e.g., 0x0 for the first slot)
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest, earliest, pending

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "0x0",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The 32-byte value stored at the requested position, zero-padded on the left

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getStorageAt",
    "params": [
      "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "0x0",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getStorageAt',
    params: ['0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', '0x0', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
console.log('Storage slot 0:', result);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

const slot0 = await provider.getStorage(address, 0);
console.log('Storage at slot 0:', slot0);

// Read a specific slot
const slot5 = await provider.getStorage(address, 5);
console.log('Storage at slot 5:', slot5);
```

```python
import requests

def get_storage_at(address, position, block='latest'):
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getStorageAt',
            'params': [address, hex(position), block],
            'id': 1
        }
    )
    return response.json()['result']

address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
value = get_storage_at(address, 0)
print(f'Storage at slot 0: {value}')

# eth_getStorageAt - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
storage = w3.eth.get_storage_at(address, 0)
print(f'Storage at slot 0: {storage.hex()}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    slot := common.BigToHash(big.NewInt(0))

    value, err := client.StorageAt(context.Background(), address, slot, nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Storage at slot 0: 0x%x\n", value)
}
```

## Common Use Cases

### 1. Verify Proxy Implementation Address

Read the EIP-1967 implementation slot to verify which contract a proxy points to on Boba Network:

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function getProxyImplementation(proxyAddress) {
  // EIP-1967 implementation slot:
  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
  const EIP1967_IMPL_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

  const value = await provider.getStorage(proxyAddress, EIP1967_IMPL_SLOT);

  // Extract the address from the 32-byte value (last 20 bytes)
  const implAddress = '0x' + value.slice(26);

  if (implAddress === '0x' + '0'.repeat(40)) {
    console.log('Not a standard EIP-1967 proxy or no implementation set');
    return null;
  }

  console.log(`Proxy: ${proxyAddress}`);
  console.log(`Implementation: ${implAddress}`);
  return implAddress;
}

// Also check the admin slot
async function getProxyAdmin(proxyAddress) {
  const EIP1967_ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
  const value = await provider.getStorage(proxyAddress, EIP1967_ADMIN_SLOT);
  return '0x' + value.slice(26);
}
```

### 2. Read Solidity Mapping Values

Calculate the storage slot for a mapping entry and read it:

```javascript
import { JsonRpcProvider, keccak256, AbiCoder, zeroPadValue, toBeHex } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function readMapping(contractAddress, mappingSlot, key) {
  // For mapping(address => uint256) at slot N:
  // slot = keccak256(abi.encode(key, N))
  const abiCoder = AbiCoder.defaultAbiCoder();
  const encoded = abiCoder.encode(
    ['address', 'uint256'],
    [key, mappingSlot]
  );
  const slot = keccak256(encoded);

  const value = await provider.getStorage(contractAddress, slot);
  return BigInt(value);
}

// Example: read an ERC-20 balance (balanceOf mapping is typically at slot 0 or 1)
const contractAddress = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';
const holderAddress = '0x1234567890abcdef1234567890abcdef12345678';
const balance = await readMapping(contractAddress, 0, holderAddress);
console.log('Token balance:', balance.toString());
```

### 3. Storage Layout Inspector

Scan multiple storage slots to analyze a contract's state:

```javascript
async function inspectStorage(provider, address, slotCount = 10) {
  console.log(`Inspecting storage for ${address} on Boba Network:`);
  console.log('─'.repeat(80));

  const results = [];
  for (let i = 0; i < slotCount; i++) {
    const value = await provider.getStorage(address, i);
    const isZero = value === '0x' + '0'.repeat(64);

    if (!isZero) {
      // Try to interpret the value
      const asNumber = BigInt(value);
      const asAddress = '0x' + value.slice(26);
      const hasAddressPattern = value.slice(2, 26) === '0'.repeat(24);

      console.log(`Slot ${i}: ${value}`);
      if (hasAddressPattern && asAddress !== '0x' + '0'.repeat(40)) {
        console.log(`  → Possible address: ${asAddress}`);
      } else {
        console.log(`  → As uint256: ${asNumber}`);
      }

      results.push({ slot: i, value, asNumber });
    }
  }

  return results;
}
```

## Best Practices

- Use known storage slot constants (EIP-1967, EIP-1822, OpenZeppelin layout) instead of guessing slot positions
- For mappings, calculate the storage slot using `keccak256(abi.encode(key, baseSlot))` per Solidity storage layout rules
- Read multiple slots in parallel when inspecting contract state to reduce total API calls
- Monitor proxy storage slots (implementation, admin, beacon) to detect unexpected upgrades
- For packed structs, understand that multiple variables may share a single 32-byte slot

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/boba-network/eth_call) - Execute a contract function call without a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/boba-network/eth_getCode) - Get the bytecode deployed at an address
- [`eth_getBalance`](https://www.dwellir.com/docs/boba-network/eth_getBalance) - Get the ETH balance of an address
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get block details for historical storage queries

---

## eth_getTransactionByHash - Boba Network RPC Method

# eth_getTransactionByHash - Boba Network RPC Method

Returns the information about a transaction by transaction hash on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getTransactionByHash` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Track pending and confirmed transaction status**: Monitor the full lifecycle of a transaction from submission through finalization on Boba Network
- **Verify transaction parameters**: Confirm the value, gas, and input data match your intent for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Wallet transaction history display**: Show detailed transaction records with sender, receiver, value, and status for end users
- **Debug failed transactions**: Inspect raw transaction data to diagnose reverted calls and unexpected behavior on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565"],
  "id": 1
}
```

## Response Fields

- `hash` (`DATA, required`): Transaction hash
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasPrice` (`QUANTITY, required`): Gas price in wei
- `input` (`DATA, required`): Transaction input data
- `nonce` (`QUANTITY, required`): Sender's nonce
- `blockHash` (`DATA, required`): Block hash (null if pending)
- `blockNumber` (`QUANTITY, required`): Block number (null if pending)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "<value>",
    "from": "<value>",
    "to": "<value>",
    "value": "0x1",
    "gas": "0x1",
    "gasPrice": "0x1",
    "input": "<value>",
    "nonce": "0x1",
    "blockHash": "<value>",
    "blockNumber": "0x1"
  }
}
```

## Common Use Cases

### 1. Wait for Transaction Confirmation with Polling

Poll `eth_getTransactionByHash` until the transaction's `blockNumber` becomes non-null, indicating it has been mined on Boba Network. This is the standard pattern for tracking transaction finality.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function waitForConfirmation(txHash, interval = 2000) {
  while (true) {
    const tx = await provider.getTransaction(txHash);

    if (tx && tx.blockNumber) {
      console.log(`Transaction confirmed in block #${tx.blockNumber}`);
      return tx;
    }

    console.log('Transaction pending, waiting...');
    await new Promise(r => setTimeout(r, interval));
  }
}

waitForConfirmation('0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565');
```

### 2. Display Transaction Details in a Wallet UI

Fetch and format transaction data for display in a wallet or explorer on Boba Network. Extract the key fields: sender, recipient, value, gas, and confirmation status.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def get_transaction_details(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if not tx:
        return {'status': 'not found'}

    return {
        'hash': tx['hash'].hex(),
        'from': tx['from'],
        'to': tx['to'],
        'value_ether': w3.from_wei(tx['value'], 'ether'),
        'gas': tx['gas'],
        'gas_price_gwei': w3.from_wei(tx['gasPrice'], 'gwei'),
        'block': tx.get('blockNumber'),
        'confirmed': tx.get('blockNumber') is not None,
    }

details = get_transaction_details('0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565')
for key, value in details.items():
    print(f'{key}: {value}')
```

### 3. Decode Transaction Input Data for Contract Interaction Analysis

When a transaction's `input` field contains encoded function calls, decode it using a contract ABI to understand what action was performed on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively.

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565")
    tx, isPending, _ := client.TransactionByHash(context.Background(), txHash)

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("From: %s\n", tx.From().Hex())
    fmt.Printf("Value: %s wei\n", tx.Value().String())
    fmt.Printf("Gas limit: %d\n", tx.Gas())

    if len(tx.Data()) > 0 {
        // First 4 bytes are the function selector
        selector := tx.Data()[:4]
        fmt.Printf("Function selector: 0x%x\n", selector)
        fmt.Printf("Input data length: %d bytes\n", len(tx.Data()))
    }
}
```

## Best Practices

- **Check if `blockNumber` is null to detect pending transactions**: A null `blockNumber` indicates the transaction has been submitted but not yet mined; use this to drive polling or loading states in your UI
- **For mined transactions, cross-reference with receipt for confirmation count**: Once a block number is available, use `eth_getTransactionReceipt` to get the final status, gas used, and emitted logs
- **Cache confirmed transaction data indefinitely**: Transaction data is immutable once mined; cache it permanently to avoid redundant RPC calls for historical lookups
- **Use `eth_getTransactionReceipt` for post-confirmation data**: After a transaction is confirmed, call `eth_getTransactionReceipt` to get gas used, logs, status code, and effective gas price

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionByHash",
    "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, formatEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565';
const tx = await provider.getTransaction(txHash);

if (tx) {
  console.log('From:', tx.from);
  console.log('To:', tx.to);
  console.log('Value:', formatEther(tx.value));
  console.log('Block:', tx.blockNumber);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565'
tx = w3.eth.get_transaction(tx_hash)

if tx:
    print(f'From: {tx["from"]}')
    print(f'To: {tx["to"]}')
    print(f'Value: {w3.from_wei(tx["value"], "ether")}')
    print(f'Block: {tx["blockNumber"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565")
    tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Pending: %v\n", isPending)
    fmt.Printf("Value: %s\n", tx.Value().String())
}
```

## Related Methods

- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/boba-network/eth_getTransactionReceipt) - Get transaction receipt
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Send transaction

---

## eth_getTransactionCount - Boba Network RPC Method

Returns the number of transactions sent from an address on Boba Network, commonly known as the **nonce**. The nonce is required for every outbound transaction to ensure correct ordering and prevent replay attacks.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getTransactionCount` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Transaction Signing** - Get the correct nonce before building and signing a new transaction on Boba Network
- **Nonce Gap Detection** - Compare `latest` and `pending` nonces to identify stuck or dropped transactions in the mempool
- **Account Activity Analysis** - Track the total number of outgoing transactions from any address on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Batch Transaction Sequencing** - Assign sequential nonces when submitting multiple transactions from the same address

## Request Parameters

- `address` (`DATA (20 bytes), required`): The address to query the transaction count for
- `blockParameter` (`QUANTITY|TAG, required`): Block number as hex, or tag: latest (confirmed nonce), pending (includes mempool txs), earliest

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "latest"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of transactions sent from the address

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: invalid address"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionCount",
    "params": [
      "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "latest"
    ],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_getTransactionCount',
    params: ['0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', 'latest'],
    id: 1
  })
});

const { result } = await response.json();
const nonce = parseInt(result, 16);
console.log('Boba Network nonce:', nonce);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000';

const nonce = await provider.getTransactionCount(address);
console.log('Confirmed nonce:', nonce);

// Get pending nonce for new transaction
const pendingNonce = await provider.getTransactionCount(address, 'pending');
console.log('Next nonce:', pendingNonce);
```

```python
import requests

def get_transaction_count(address, block='latest'):
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_getTransactionCount',
            'params': [address, block],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

address = '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000'
nonce = get_transaction_count(address)
print(f'Boba Network nonce: {nonce}')

pending_nonce = get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending_nonce}')

# eth_getTransactionCount - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
nonce = w3.eth.get_transaction_count(address)
print(f'Nonce: {nonce}')

pending = w3.eth.get_transaction_count(address, 'pending')
print(f'Pending nonce: {pending}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    address := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")

    // Get confirmed nonce
    nonce, err := client.NonceAt(context.Background(), address, nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Boba Network nonce: %d\n", nonce)

    // Get pending nonce for new transaction
    pendingNonce, err := client.PendingNonceAt(context.Background(), address)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Pending nonce: %d\n", pendingNonce)
}
```

## Common Use Cases

### 1. Safe Transaction Sender with Nonce Management

Build a transaction sender that handles nonces correctly on Boba Network:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

class TransactionSender {
  constructor(privateKey) {
    this.wallet = new Wallet(privateKey, provider);
    this.localNonce = null;
  }

  async getNextNonce() {
    // Get the pending nonce from the network
    const pendingNonce = await provider.getTransactionCount(
      this.wallet.address, 'pending'
    );

    // Use whichever is higher: local tracker or network pending
    if (this.localNonce === null || pendingNonce > this.localNonce) {
      this.localNonce = pendingNonce;
    }

    const nonce = this.localNonce;
    this.localNonce++;
    return nonce;
  }

  async sendTransaction(to, valueEth) {
    const nonce = await this.getNextNonce();

    const tx = await this.wallet.sendTransaction({
      to,
      value: parseEther(valueEth),
      nonce
    });

    console.log(`Sent tx ${tx.hash} with nonce ${nonce}`);
    return tx;
  }

  // Reset local nonce tracker (e.g., after errors)
  resetNonce() {
    this.localNonce = null;
  }
}
```

### 2. Stuck Transaction Detector

Detect and report stuck transactions by comparing nonces:

```javascript
async function detectStuckTransactions(provider, address) {
  const confirmedNonce = await provider.getTransactionCount(address, 'latest');
  const pendingNonce = await provider.getTransactionCount(address, 'pending');

  const pendingCount = pendingNonce - confirmedNonce;

  if (pendingCount === 0) {
    console.log('No pending transactions');
    return { status: 'clear', pendingCount: 0 };
  }

  console.log(`${pendingCount} pending transaction(s) detected`);
  console.log(`Confirmed nonce: ${confirmedNonce}`);
  console.log(`Pending nonce: ${pendingNonce}`);
  console.log(`Stuck nonces: ${confirmedNonce} to ${pendingNonce - 1}`);

  return {
    status: 'stuck',
    pendingCount,
    confirmedNonce,
    pendingNonce,
    stuckNonces: Array.from(
      { length: pendingCount },
      (_, i) => confirmedNonce + i
    )
  };
}

// Usage
const result = await detectStuckTransactions(provider, '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000');
if (result.status === 'stuck') {
  console.log('Consider replacing transactions at nonces:', result.stuckNonces);
}
```

### 3. Batch Transaction Submitter

Submit multiple transactions in sequence with correct nonce ordering:

```javascript
async function sendBatchTransactions(wallet, transactions) {
  const startNonce = await provider.getTransactionCount(
    wallet.address, 'pending'
  );

  console.log(`Sending ${transactions.length} transactions starting at nonce ${startNonce}`);

  const receipts = [];
  for (let i = 0; i < transactions.length; i++) {
    const nonce = startNonce + i;
    const tx = await wallet.sendTransaction({
      ...transactions[i],
      nonce
    });

    console.log(`Tx ${i + 1}/${transactions.length} sent: ${tx.hash} (nonce: ${nonce})`);
    receipts.push(tx);
  }

  // Wait for all to confirm
  const confirmed = await Promise.all(
    receipts.map(tx => tx.wait())
  );

  console.log(`All ${confirmed.length} transactions confirmed`);
  return confirmed;
}

// Usage
const transactions = [
  { to: '0xRecipient1...', value: parseEther('0.1') },
  { to: '0xRecipient2...', value: parseEther('0.2') },
  { to: '0xRecipient3...', value: parseEther('0.05') }
];

await sendBatchTransactions(wallet, transactions);
```

## Best Practices

- Always use `pending` block tag when fetching the nonce for a new transaction to avoid nonce collisions
- Track nonces locally with a monotonic counter that syncs with the pending nonce from the network on reset
- If `pending` nonce equals `latest` nonce, no stuck transactions exist -- the account is clean
- For high-throughput applications (multiple transactions per second), implement a local nonce manager with retry logic
- The nonce starts at 0 for new accounts and increments by 1 for each confirmed outbound transaction

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Send a signed transaction to the network
- [`eth_getBalance`](https://www.dwellir.com/docs/boba-network/eth_getBalance) - Get the ETH balance of an address
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/boba-network/eth_getTransactionByHash) - Get transaction details by hash
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/boba-network/eth_getTransactionReceipt) - Get the receipt of a confirmed transaction

---

## eth_getTransactionReceipt - Boba Network RPC Method

# eth_getTransactionReceipt - Boba Network RPC Method

Returns the receipt of a transaction by transaction hash on Boba Network. Receipt is only available for mined transactions.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_getTransactionReceipt` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Transaction confirmation with success/failure status**: Verify that a transaction has been mined on Boba Network and determine whether it succeeded or reverted
- **Gas usage analysis**: Compare actual gas consumed against pre-transaction estimates for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Event log parsing from emitted events**: Extract and decode contract events for indexing, analytics, and notification systems
- **Contract deployment detection**: Identify newly deployed contracts on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively by checking the `contractAddress` field

## Request Parameters

- `transactionHash` (`DATA, required`): 32-byte transaction hash

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565"],
  "id": 1
}
```

## Response Fields

- `status` (`QUANTITY, required`): 1 (success) or 0 (failure)
- `transactionHash` (`DATA, required`): Transaction hash
- `blockHash` (`DATA, required`): Block hash
- `blockNumber` (`QUANTITY, required`): Block number
- `gasUsed` (`QUANTITY, required`): Gas used by this transaction
- `cumulativeGasUsed` (`QUANTITY, required`): Total gas used in block up to this tx
- `logs` (`Array, required`): Array of log objects
- `contractAddress` (`DATA, required`): Created contract address (if deployment)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "status": "0x1",
    "transactionHash": "<value>",
    "blockHash": "<value>",
    "blockNumber": "0x1",
    "gasUsed": "0x1",
    "cumulativeGasUsed": "0x1",
    "logs": [],
    "contractAddress": "<value>"
  }
}
```

## Common Use Cases

### 1. Confirm Transaction Success and Parse Emitted Events

Poll `eth_getTransactionReceipt` after submitting a transaction to confirm it was mined successfully on Boba Network. Once available, iterate through the `logs` array to decode and process events emitted by the transaction.

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function confirmAndParse(txHash) {
  let receipt = null;

  while (!receipt) {
    receipt = await provider.getTransactionReceipt(txHash);
    if (!receipt) {
      console.log('Waiting for confirmation...');
      await new Promise(r => setTimeout(r, 2000));
    }
  }

  const status = receipt.status === 1 ? 'Success' : 'Failed';
  console.log(`Transaction ${status} in block #${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
  console.log(`Events emitted: ${receipt.logs.length}`);

  for (const log of receipt.logs) {
    console.log(`  Event from ${log.address} with topics:`, log.topics);
  }

  return receipt;
}

confirmAndParse('0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565');
```

### 2. Detect Contract Deployments by Checking contractAddress

When monitoring the chain for new contract deployments on Boba Network, check the `contractAddress` field in transaction receipts. A non-null value indicates a contract creation transaction.

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def detect_deployment(tx_hash):
    receipt = w3.eth.get_transaction_receipt(tx_hash)

    if not receipt:
        print(f'Transaction {tx_hash} not yet mined')
        return None

    if receipt['contractAddress']:
        print(f'Contract deployed at: {receipt["contractAddress"]}')
        print(f'Deployer: {receipt["from"]}')
        print(f'Gas used: {receipt["gasUsed"]}')
        return receipt['contractAddress']
    else:
        print(f'Transaction {tx_hash} is not a contract deployment')
        return None

detect_deployment('0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565')
```

### 3. Compute Effective Gas Price Paid by the Sender

Use the `effectiveGasPrice` field from the receipt (available on EIP-1559 chains) to calculate the actual cost of a transaction on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively. Compare this against the gas price from the transaction object for fee analysis.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")

    txHash := common.HexToHash("0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565")
    receipt, _ := client.TransactionReceipt(context.Background(), txHash)

    if receipt == nil {
        log.Fatal("Transaction not yet mined")
    }

    status := "Success"
    if receipt.Status == 0 {
        status = "Failed"
    }

    fmt.Printf("Status: %s\n", status)
    fmt.Printf("Block: %d\n", receipt.BlockNumber.Uint64())
    fmt.Printf("Gas used: %d\n", receipt.GasUsed)

    // Calculate total cost: gasUsed * effectiveGasPrice
    totalCost := new(big.Int).Mul(
        big.NewInt(int64(receipt.GasUsed)),
        receipt.EffectiveGasPrice,
    )
    fmt.Printf("Total cost: %s wei\n", totalCost.String())

    if receipt.ContractAddress != (common.Address{}) {
        fmt.Printf("Contract deployed at: %s\n", receipt.ContractAddress.Hex())
    }
}
```

## Best Practices

- **Receipt is only available after mining; poll until non-null**: A `null` response means the transaction is pending or not found; implement a polling loop with exponential backoff to wait for confirmation
- **Check the `status` field**: `0x1` indicates a successful execution; `0x0` means the transaction reverted and may have consumed all provided gas
- **Parse the `logs` array for events using known topic hashes**: Each log entry contains up to 4 indexed topics and a data field; use ABI definitions to decode them into readable event parameters
- **For frontrunning detection, compare effective gas price across similar transactions**: Monitoring the `effectiveGasPrice` across transactions in a block can reveal priority gas auction dynamics on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively
- **`contractAddress` is non-null only for contract deployment transactions**: Use this field to distinguish regular transfers from contract create operations

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getTransactionReceipt",
    "params": ["0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const txHash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565';
const receipt = await provider.getTransactionReceipt(txHash);

if (receipt) {
  console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
  console.log('Gas Used:', receipt.gasUsed.toString());
  console.log('Block:', receipt.blockNumber);
  console.log('Logs:', receipt.logs.length);

  // Parse specific events
  for (const log of receipt.logs) {
    console.log('Event from:', log.address);
  }
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

tx_hash = '0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565'
receipt = w3.eth.get_transaction_receipt(tx_hash)

if receipt:
    status = 'Success' if receipt['status'] == 1 else 'Failed'
    print(f'Status: {status}')
    print(f'Gas Used: {receipt["gasUsed"]}')
    print(f'Block: {receipt["blockNumber"]}')
    print(f'Logs: {len(receipt["logs"])}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    txHash := common.HexToHash("0x5d01c1b5655ee1315a49968be5bf94b730e8ea3854e88b3ee4ef7624f3b15565")
    receipt, err := client.TransactionReceipt(context.Background(), txHash)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", receipt.Status)
    fmt.Printf("Gas Used: %d\n", receipt.GasUsed)
    fmt.Printf("Logs: %d\n", len(receipt.Logs))
}
```

## Related Methods

- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/boba-network/eth_getTransactionByHash) - Get transaction details
- [`eth_getLogs`](https://www.dwellir.com/docs/boba-network/eth_getLogs) - Query logs by filter

---

## eth_hashrate - Boba Network RPC Method

Returns the legacy `eth_hashrate` compatibility value on Boba Network. Depending on the client behind the endpoint, this call may return a hex quantity like `0x0` or a method-not-found style error.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

> **Note:** Treat `eth_hashrate` as a node-local mining metric and compatibility value. Public endpoints often report `0x0` or reject the method entirely, so it is not a reliable chain-health or consensus signal.

## When to Use This Method

`eth_hashrate` is relevant for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Node Diagnostics** - Check whether the connected client reports a legacy hashrate metric
- **Client Capability Checks** - Distinguish between `0x0`, unsupported-method, and method-not-found responses
- **Dashboard Integration** - Display the metric alongside other node status data when it is available

## Best Practices

- Returns `0x0` on proof-of-stake chains (post-Merge) and most modern deployments
- Not relevant for most production environments; treat as informational only
- Do not rely on this method for chain health or consensus signals
- Use eth\_syncing or eth\_blockNumber for reliable node status monitoring

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_hashrate",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the reported compatibility value when the client exposes the method

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_hashrate",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_hashrate',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_hashrate unsupported:', payload.error.message);
} else {
  console.log('Boba Network hashrate:', parseInt(payload.result, 16), 'H/s');
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const hashrate = await provider.send('eth_hashrate', []);
  console.log('Boba Network hashrate:', parseInt(hashrate, 16), 'H/s');
} catch (error) {
  console.log('eth_hashrate unsupported:', error.message);
}
```

```python
import requests

def get_hashrate():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_hashrate',
            'params': [],
            'id': 1
        }
    )
    payload = response.json()
    if 'error' in payload:
        raise RuntimeError(payload['error']['message'])
    return int(payload['result'], 16)

hashrate = get_hashrate()
print(f'Boba Network hashrate: {hashrate} H/s')

# eth_hashrate - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Boba Network hashrate: {w3.eth.hashrate} H/s')
except Exception as exc:
    print(f'eth_hashrate unsupported: {exc}')
```

```go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_hashrate")
    if err != nil {
        log.Println("eth_hashrate unsupported:", err)
        return
    }

    fmt.Printf("Boba Network hashrate: %s\n", result)
}
```

## Common Use Cases

### 1. Compatibility-Aware Status Dashboard

Display the reported compatibility value alongside other client status signals without assuming the method is always available:

```javascript
async function getMiningStats(provider) {
  const blockNumber = await provider.getBlockNumber();
  let hashrate = null;
  let supported = true;

  try {
    hashrate = await provider.send('eth_hashrate', []);
  } catch (error) {
    supported = false;
  }

  return {
    supported,
    hashrate: hashrate ? parseInt(hashrate, 16) : null,
    currentBlock: blockNumber
  };
}
```

### 2. Capability Check

Check whether the connected client reports `eth_hashrate` at all:

```javascript
async function getHashrateStatus(provider) {
  try {
    const hashrate = await provider.send('eth_hashrate', []);
    return { supported: true, raw: hashrate, isZero: hashrate === '0x0' };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

console.log(await getHashrateStatus(provider));
```

zing - retry after delay |
\| -32601 | Method not found | Node client may not support this method |
\| -32005 | Rate limit exceeded | Reduce polling frequency |

## Related Methods

- [`eth_mining`](https://www.dwellir.com/docs/boba-network/eth_mining) - Check if the node is actively mining
- [`eth_coinbase`](https://www.dwellir.com/docs/boba-network/eth_coinbase) - Get the mining/coinbase address
- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get the current block height

---

## eth_maxPriorityFeePerGas - Boba Network RPC Method

Returns the current suggested priority fee (tip) per gas in wei on Boba Network. This is the amount paid directly to validators to incentivize faster transaction inclusion in EIP-1559 compatible blocks.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_maxPriorityFeePerGas` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **EIP-1559 Transaction Building** - Get the recommended tip to include in `maxPriorityFeePerGas` when constructing type-2 transactions on Boba Network
- **Fee Optimization** - Balance transaction speed against cost by adjusting the priority fee relative to the suggested value
- **Time-Sensitive Transactions** - Increase the tip above the suggested value for DEX swaps, liquidations, or other latency-sensitive operations on AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **Gas Price Estimation** - Combine with `baseFeePerGas` from the latest block to calculate the total `maxFeePerGas` for accurate fee estimation

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the suggested priority fee per gas in wei

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x3B9ACA00"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method eth_maxPriorityFeePerGas does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_maxPriorityFeePerGas",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_maxPriorityFeePerGas',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const priorityFeeWei = BigInt(result);
const priorityFeeGwei = Number(priorityFeeWei) / 1e9;
console.log('Boba Network priority fee:', priorityFeeGwei, 'Gwei');

// Using ethers.js
import { JsonRpcProvider, formatUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const feeData = await provider.getFeeData();
console.log('Max Priority Fee:', formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'Gwei');
console.log('Max Fee Per Gas:', formatUnits(feeData.maxFeePerGas, 'gwei'), 'Gwei');
```

```python
import requests

def get_max_priority_fee():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_maxPriorityFeePerGas',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

priority_fee_wei = get_max_priority_fee()
priority_fee_gwei = priority_fee_wei / 1e9
print(f'Boba Network priority fee: {priority_fee_gwei} Gwei')

# eth_maxPriorityFeePerGas - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
priority_fee = w3.eth.max_priority_fee
print(f'Max Priority Fee: {w3.from_wei(priority_fee, "gwei")} Gwei')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    tip, err := client.SuggestGasTipCap(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    gwei := new(big.Float).Quo(new(big.Float).SetInt(tip), big.NewFloat(1e9))
    fmt.Printf("Boba Network priority fee: %s Gwei\n", gwei.Text('f', 4))
}
```

## Common Use Cases

### 1. Build an EIP-1559 Transaction

Construct a properly priced type-2 transaction on Boba Network:

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

async function buildEIP1559Transaction(privateKey, to, valueEth) {
  const wallet = new Wallet(privateKey, provider);

  // Get current fee data
  const feeData = await provider.getFeeData();
  const latestBlock = await provider.getBlock('latest');
  const baseFee = latestBlock.baseFeePerGas;

  // Set maxPriorityFeePerGas from the suggestion
  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;

  // maxFeePerGas = 2 * baseFee + maxPriorityFeePerGas (buffer for base fee increases)
  const maxFeePerGas = baseFee * 2n + maxPriorityFeePerGas;

  const tx = await wallet.sendTransaction({
    to,
    value: parseEther(valueEth),
    type: 2,
    maxPriorityFeePerGas,
    maxFeePerGas
  });

  console.log('Sent with priority fee:', Number(maxPriorityFeePerGas) / 1e9, 'Gwei');
  return tx;
}
```

### 2. Dynamic Fee Strategy

Adjust priority fees based on transaction urgency:

```javascript
async function getFeeByUrgency(provider, urgency = 'standard') {
  const feeData = await provider.getFeeData();
  const basePriorityFee = feeData.maxPriorityFeePerGas;

  const multipliers = {
    low: 0.8,       // Willing to wait
    standard: 1.0,  // Normal speed
    fast: 1.5,      // Faster inclusion
    urgent: 2.0     // Next-block target
  };

  const multiplier = multipliers[urgency] || 1.0;
  const adjustedFee = BigInt(Math.ceil(Number(basePriorityFee) * multiplier));

  const block = await provider.getBlock('latest');
  const maxFeePerGas = block.baseFeePerGas * 2n + adjustedFee;

  return {
    maxPriorityFeePerGas: adjustedFee,
    maxFeePerGas
  };
}

// Usage
const fees = await getFeeByUrgency(provider, 'fast');
console.log('Priority fee:', Number(fees.maxPriorityFeePerGas) / 1e9, 'Gwei');
console.log('Max fee:', Number(fees.maxFeePerGas) / 1e9, 'Gwei');
```

### 3. Priority Fee Monitor

Track priority fee changes over time on Boba Network:

```javascript
async function monitorPriorityFee(provider, interval = 12000) {
  let previousFee = null;

  setInterval(async () => {
    const feeData = await provider.getFeeData();
    const currentFee = feeData.maxPriorityFeePerGas;
    const feeGwei = Number(currentFee) / 1e9;

    if (previousFee !== null) {
      const change = Number(currentFee - previousFee) / Number(previousFee) * 100;
      const direction = change > 0 ? 'UP' : change < 0 ? 'DOWN' : 'STABLE';
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei (${direction} ${Math.abs(change).toFixed(1)}%)`);
    } else {
      console.log(`Priority fee: ${feeGwei.toFixed(4)} Gwei`);
    }

    previousFee = currentFee;
  }, interval);
}
```

## Best Practices

- Use `eth_feeHistory` with percentile rewards for a more accurate priority fee estimate than the node's suggestion alone
- The suggested priority fee is the minimum for timely inclusion -- multiply by 1.5-2x for faster confirmation on congested networks
- Fall back to `eth_gasPrice` if `eth_maxPriorityFeePerGas` returns method not found (-32601) on non-EIP-1559 nodes
- For L2 chains, the priority fee is typically much lower than L1 -- never reuse L1 gas parameters on L2
- Cache with a short TTL (12 seconds, one block time) since priority fees change with each block

## Related Methods

- [`eth_gasPrice`](https://www.dwellir.com/docs/boba-network/eth_gasPrice) - Get the legacy gas price
- [`eth_feeHistory`](https://www.dwellir.com/docs/boba-network/eth_feeHistory) - Get historical fee data for trend analysis
- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas units required for a transaction
- [`eth_getBlockByNumber`](https://www.dwellir.com/docs/boba-network/eth_getBlockByNumber) - Get block details including `baseFeePerGas`

---

## eth_mining - Boba Network RPC Method

Checks the legacy `eth_mining` compatibility method on Boba Network. Depending on the client behind the endpoint, this call may return a boolean, `unimplemented`, or a method-not-found style error.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

> **Note:** `eth_mining` is best treated as a node-local diagnostic and compatibility probe. Public endpoints often return `false`, `unimplemented`, or a method-not-found error, so it is not a reliable chain-health signal.

## When to Use This Method

`eth_mining` is relevant for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Node Capability Checks** - Verify whether the connected client still exposes this legacy method
- **Migration Audits** - Remove assumptions that Ethereum-era mining RPCs always return a boolean
- **Fallback Design** - Switch dashboards and health probes to `eth_syncing`, `eth_blockNumber`, or `net_version`

## Best Practices

- Returns `false` on proof-of-stake chains (post-Merge) and most modern chains
- Not relevant for provider-managed nodes; do not depend on this for production monitoring
- Use eth\_syncing for general node health checks instead
- Treat unsupported-method and unimplemented responses as a normal condition

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_mining",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): Legacy boolean compatibility value when the connected client still exposes eth_mining

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "unimplemented"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_mining",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_mining',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('eth_mining unsupported:', payload.error.message);
} else {
  console.log('Boba Network mining:', payload.result);
}

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
try {
  const mining = await provider.send('eth_mining', []);
  console.log('Boba Network mining:', mining);
} catch (error) {
  console.log('eth_mining unsupported:', error.message);
}
```

```python
import requests

def is_mining():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_mining',
            'params': [],
            'id': 1
        }
    )
    return response.json()

mining = is_mining()
if 'error' in mining:
    print(f"eth_mining unsupported: {mining['error']['message']}")
else:
    print(f'Boba Network mining: {mining["result"]}')

# eth_mining - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
try:
    print(f'Boba Network mining: {w3.eth.mining}')
except Exception as exc:
    print(f'eth_mining unsupported: {exc}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var isMining bool
    err = client.CallContext(context.Background(), &isMining, "eth_mining")
    if err != nil {
        log.Println("eth_mining unsupported:", err)
        return
    }

    fmt.Println("Boba Network mining:", isMining)
}
```

## Common Use Cases

### 1. Capability-Aware Health Check

Combine `eth_mining` with other status RPCs, but treat unsupported responses as normal:

```javascript
async function getNodeStatus(provider) {
  const [syncing, blockNumber] = await Promise.all([
    provider.send('eth_syncing', []),
    provider.getBlockNumber()
  ]);

  let miningStatus = { supported: false };
  try {
    miningStatus = {
      supported: true,
      result: await provider.send('eth_mining', [])
    };
  } catch {}

  return {
    miningStatus,
    isSynced: syncing === false,
    currentBlock: blockNumber
  };
}
```

### 2. Client Compatibility Check

Check whether the connected client still implements the legacy method:

```javascript
async function checkEthMiningSupport(provider) {
  try {
    const mining = await provider.send('eth_mining', []);
    return { supported: true, mining };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}

const status = await checkEthMiningSupport(provider);
console.log(status);
```

### 3. Recommended Alternatives

For production dashboards and health checks, prefer:

- `eth_blockNumber` for liveness
- `eth_syncing` for sync state
- `net_version` or `eth_chainId` for endpoint identity

## Related Methods

- [`eth_hashrate`](https://www.dwellir.com/docs/boba-network/eth_hashrate) - Get the mining hash rate
- [`eth_coinbase`](https://www.dwellir.com/docs/boba-network/eth_coinbase) - Get the coinbase/block producer address
- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get the current block height

---

## eth_newBlockFilter - Boba Network RPC Method

Creates a filter on Boba Network that notifies when new blocks arrive. Once created, poll the filter with `eth_getFilterChanges` to receive an array of block hashes for each new block added to the chain since your last poll.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_newBlockFilter` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Block Monitoring** - Detect new blocks on Boba Network as they are mined or finalized, without repeatedly calling `eth_blockNumber`
- **Chain Progression Tracking** - Build dashboards or alerting systems that track block production rate and timing
- **Reorg Detection** - Identify chain reorganizations by monitoring for blocks that are replaced or missing
- **Event-Driven Architectures** - Trigger downstream processing (indexing, notifications, settlement) whenever a new block appears

## Best Practices

- Use WebSocket subscriptions (`eth_subscribe`) for real-time block notifications in production environments
- Poll with `eth_getFilterChanges` at 1-5 second intervals to receive new block hashes
- Combine with `eth_getBlockByNumber` or `eth_getBlockByHash` to retrieve full block details

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newBlockFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for new blocks via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte block hashes for each new block since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newBlockFilter - Boba Network RPC Method
FILTER_ID=$(curl -s -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newBlockFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for new blocks
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a block filter
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Block filter created:', filterId);

// Poll for new blocks
async function pollNewBlocks(interval = 2000) {
  while (true) {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (blockHashes.length > 0) {
      for (const hash of blockHashes) {
        const block = await provider.getBlock(hash);
        console.log(`New block #${block.number} (${hash})`);
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollNewBlocks();
```

```python
import requests
import time

RPC_URL = 'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create block filter
filter_result = rpc_call('eth_newBlockFilter', [])
filter_id = filter_result['result']
print(f'Block filter created: {filter_id}')

# Poll for new blocks
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    block_hashes = changes.get('result', [])
    if block_hashes:
        for block_hash in block_hashes:
            print(f'New block: {block_hash}')
    time.sleep(2)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to new block headers
    headers := make(chan *types.Header)
    sub, err := client.SubscribeNewHead(context.Background(), headers)
    if err != nil {
        // Fallback: polling approach
        lastBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(2 * time.Second)

        for range ticker.C {
            currentBlock, err := client.BlockNumber(context.Background())
            if err != nil {
                log.Println("Error:", err)
                continue
            }
            if currentBlock > lastBlock {
                for b := lastBlock + 1; b <= currentBlock; b++ {
                    block, _ := client.BlockByNumber(context.Background(), new(big.Int).SetUint64(b))
                    if block != nil {
                        fmt.Printf("New block #%d: %s\n", block.Number().Uint64(), block.Hash().Hex())
                    }
                }
                lastBlock = currentBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case header := <-headers:
            fmt.Printf("New block #%d: %s\n", header.Number.Uint64(), header.Hash().Hex())
        }
    }
}
```

## Common Use Cases

### 1. Block Production Rate Monitor

Track block times and detect slowdowns on Boba Network:

```javascript
async function monitorBlockRate(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  let lastBlockTime = null;
  const blockTimes = [];

  setInterval(async () => {
    const blockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of blockHashes) {
      const block = await provider.getBlock(hash);
      const blockTime = block.timestamp;

      if (lastBlockTime) {
        const interval = blockTime - lastBlockTime;
        blockTimes.push(interval);

        // Keep last 100 block times
        if (blockTimes.length > 100) blockTimes.shift();

        const avgBlockTime = blockTimes.reduce((a, b) => a + b, 0) / blockTimes.length;
        console.log(`Block #${block.number} | interval: ${interval}s | avg: ${avgBlockTime.toFixed(1)}s`);

        if (interval > avgBlockTime * 3) {
          console.warn(`Slow block detected - ${interval}s vs ${avgBlockTime.toFixed(1)}s average`);
        }
      }
      lastBlockTime = blockTime;
    }
  }, 2000);
}
```

### 2. Reorg-Aware Block Tracker

Detect chain reorganizations by tracking block hashes:

```javascript
async function trackBlocksWithReorgDetection(provider) {
  const filterId = await provider.send('eth_newBlockFilter', []);
  const blockHistory = new Map(); // blockNumber -> blockHash

  setInterval(async () => {
    const newBlockHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of newBlockHashes) {
      const block = await provider.getBlock(hash);
      const existingHash = blockHistory.get(block.number);

      if (existingHash && existingHash !== hash) {
        console.warn(`Reorg detected at block #${block.number}!`);
        console.warn(`  Old hash: ${existingHash}`);
        console.warn(`  New hash: ${hash}`);
        // Trigger reorg handling logic
      }

      blockHistory.set(block.number, hash);

      // Prune old entries
      if (blockHistory.size > 1000) {
        const minBlock = block.number - 500;
        for (const [num] of blockHistory) {
          if (num < minBlock) blockHistory.delete(num);
        }
      }
    }
  }, 2000);
}
```

### 3. Block-Triggered Task Scheduler

Execute tasks whenever a new block is produced:

```javascript
async function onNewBlock(provider, callback) {
  const filterId = await provider.send('eth_newBlockFilter', []);

  const poll = async () => {
    try {
      const hashes = await provider.send('eth_getFilterChanges', [filterId]);
      for (const hash of hashes) {
        await callback(hash);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        return provider.send('eth_newBlockFilter', []).then(newId => {
          console.log('Block filter recreated');
          return newId;
        });
      }
      throw error;
    }
    return filterId;
  };

  let currentFilterId = filterId;
  setInterval(async () => {
    currentFilterId = await poll() || currentFilterId;
  }, 2000);
}

// Usage
onNewBlock(provider, async (blockHash) => {
  console.log(`Processing block: ${blockHash}`);
  // Run indexer, check conditions, send notifications, etc.
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll this filter for new block hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/boba-network/eth_uninstallFilter) - Remove the block filter when no longer needed
- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get the current block number (alternative to filter-based monitoring)
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/boba-network/eth_getBlockByHash) - Fetch full block details for hashes returned by the filter

---

## eth_newFilter - Boba Network RPC Method

Creates a filter object on Boba Network based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_newFilter` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Event Monitoring** - Subscribe to specific contract events on Boba Network such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newFilter - Boba Network RPC Method
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Boba Network:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/boba-network/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/boba-network/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/boba-network/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/boba-network/eth_newBlockFilter) - Create a filter for new block notifications instead of logs

---

## eth_newPendingTransactionFilter - Boba Network RPC Method

Creates a filter on Boba Network that notifies when new pending transactions are added to the mempool. Once created, poll the filter with `eth_getFilterChanges` to receive an array of transaction hashes for pending transactions that have appeared since your last poll.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_newPendingTransactionFilter` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Mempool Monitoring** - Observe unconfirmed transactions on Boba Network to understand network activity and congestion
- **Transaction Tracking** - Detect when a specific transaction enters the mempool before it is mined
- **MEV Opportunity Detection** - Identify arbitrage, liquidation, or sandwich opportunities by watching pending transactions
- **Gas Price Estimation** - Analyze pending transactions to estimate optimal gas pricing for AI-powered dApps, Web2 API integration, enterprise blockchain solutions, and offchain computation

## Best Practices

- High data volume from mempool monitoring requires efficient handling; filter and process selectively
- Use WebSocket `eth_subscribe("newPendingTransactions")` for production-grade pending transaction monitoring
- Pending filter results may include transactions that are never mined; do not treat them as confirmed

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newPendingTransactionFilter",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for pending transactions via eth_getFilterChanges
- `result` (`Array<DATA>, required`): Array of 32-byte transaction hashes for pending transactions since the last poll

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2a1b3c"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_newPendingTransactionFilter - Boba Network RPC Method
FILTER_ID=$(curl -s -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newPendingTransactionFilter",
    "params": [],
    "id": 1
  }' | jq -r '.result')

echo "Filter ID: $FILTER_ID"

# Poll for pending transactions
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterChanges\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a pending transaction filter
const filterId = await provider.send('eth_newPendingTransactionFilter', []);
console.log('Pending tx filter created:', filterId);

// Poll for pending transactions
async function pollPendingTxs(interval = 1000) {
  while (true) {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    if (txHashes.length > 0) {
      console.log(`${txHashes.length} new pending transactions`);
      for (const hash of txHashes) {
        const tx = await provider.getTransaction(hash);
        if (tx) {
          console.log(`  ${hash} | to: ${tx.to} | value: ${tx.value}`);
        }
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}

pollPendingTxs();
```

```python
import requests
import time

RPC_URL = 'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create pending transaction filter
filter_result = rpc_call('eth_newPendingTransactionFilter', [])
filter_id = filter_result['result']
print(f'Pending tx filter created: {filter_id}')

# Poll for pending transactions
while True:
    changes = rpc_call('eth_getFilterChanges', [filter_id])
    tx_hashes = changes.get('result', [])
    if tx_hashes:
        print(f'{len(tx_hashes)} new pending transactions')
        for tx_hash in tx_hashes[:5]:  # Show first 5
            tx = rpc_call('eth_getTransactionByHash', [tx_hash])
            tx_data = tx.get('result', {})
            if tx_data:
                print(f'  {tx_hash} | to: {tx_data.get("to")} | value: {tx_data.get("value")}')
    time.sleep(1)
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Subscribe to pending transactions
    pendingTxs := make(chan common.Hash)
    sub, err := client.SubscribePendingTransactions(context.Background(), pendingTxs)
    if err != nil {
        log.Fatal("Subscription failed:", err)
    }

    fmt.Println("Monitoring pending transactions on Boba Network...")

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case txHash := <-pendingTxs:
            tx, isPending, err := client.TransactionByHash(context.Background(), txHash)
            if err == nil && isPending {
                fmt.Printf("Pending tx: %s | to: %s | value: %s\n",
                    txHash.Hex(), tx.To().Hex(), tx.Value().String())
            }
        }
    }
}
```

## Common Use Cases

### 1. Mempool Activity Dashboard

Monitor mempool throughput and transaction types on Boba Network:

```javascript
async function mempoolDashboard(provider) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);

  const stats = {
    totalSeen: 0,
    contractCalls: 0,
    transfers: 0,
    intervalStart: Date.now()
  };

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);
    stats.totalSeen += txHashes.length;

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        if (tx.data && tx.data !== '0x') {
          stats.contractCalls++;
        } else {
          stats.transfers++;
        }
      } catch (e) {
        // Transaction may have been mined or dropped
      }
    }

    const elapsed = (Date.now() - stats.intervalStart) / 1000;
    const txPerSec = (stats.totalSeen / elapsed).toFixed(1);
    console.log(`Mempool: ${stats.totalSeen} txs (${txPerSec}/s) | Calls: ${stats.contractCalls} | Transfers: ${stats.transfers}`);
  }, 2000);
}
```

### 2. Track Specific Address Activity

Watch for pending transactions involving a target address:

```javascript
async function watchAddress(provider, targetAddress) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const target = targetAddress.toLowerCase();

  console.log(`Watching pending transactions for ${targetAddress}...`);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx) continue;

        const isFrom = tx.from?.toLowerCase() === target;
        const isTo = tx.to?.toLowerCase() === target;

        if (isFrom || isTo) {
          console.log(`Pending tx detected for ${targetAddress}:`);
          console.log(`  Hash: ${hash}`);
          console.log(`  Direction: ${isFrom ? 'OUTGOING' : 'INCOMING'}`);
          console.log(`  Value: ${tx.value} wei`);
          console.log(`  Gas price: ${tx.gasPrice} wei`);
        }
      } catch (e) {
        // Transaction already mined or dropped
      }
    }
  }, 1000);
}
```

### 3. Large Transaction Alert System

Detect high-value pending transactions:

```javascript
async function largeTransactionAlerts(provider, thresholdEth = 10) {
  const filterId = await provider.send('eth_newPendingTransactionFilter', []);
  const thresholdWei = BigInt(thresholdEth * 1e18);

  setInterval(async () => {
    const txHashes = await provider.send('eth_getFilterChanges', [filterId]);

    for (const hash of txHashes) {
      try {
        const tx = await provider.getTransaction(hash);
        if (!tx || !tx.value) continue;

        if (BigInt(tx.value) >= thresholdWei) {
          const ethValue = Number(BigInt(tx.value)) / 1e18;
          console.log(`LARGE TX: ${ethValue.toFixed(4)} ETH`);
          console.log(`  From: ${tx.from}`);
          console.log(`  To: ${tx.to}`);
          console.log(`  Hash: ${hash}`);
        }
      } catch (e) {
        // Transaction may have already been mined
      }
    }
  }, 1000);
}
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll this filter for new pending transaction hashes
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/boba-network/eth_uninstallFilter) - Remove the filter when no longer needed
- [`eth_getTransactionByHash`](https://www.dwellir.com/docs/boba-network/eth_getTransactionByHash) - Fetch full transaction details for hashes returned by the filter
- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Submit a transaction that will appear in the pending pool

---

## eth_protocolVersion - Boba Network RPC Method

Returns the current Ethereum protocol version used by the Boba Network node.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_protocolVersion` is useful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Client Compatibility** - Verify that a node supports the protocol version your application requires
- **Version-Gated Features** - Enable or disable features based on the protocol version (e.g., EIP-1559 support)
- **Multi-Client Environments** - Ensure consistent protocol versions across a fleet of nodes
- **Debugging** - Diagnose issues caused by protocol version mismatches between clients

## Best Practices

- Modern clients often return a static value; do not rely on this method for feature detection
- This method is not used for transaction signing; use `eth_chainId` for network identification
- Many post-Merge clients no longer support this method; handle unsupported-method errors gracefully

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_protocolVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`STRING, required`): The current Ethereum protocol version as a string (e.g., "0x41" for protocol version 65)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x41"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "the method eth_protocolVersion does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_protocolVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_protocolVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
const version = parseInt(result, 16);
console.log('Boba Network protocol version:', version);

// Using ethers.js
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const protocolVersion = await provider.send('eth_protocolVersion', []);
console.log('Boba Network protocol version:', parseInt(protocolVersion, 16));
```

```python
import requests

def get_protocol_version():
    response = requests.post(
        'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'eth_protocolVersion',
            'params': [],
            'id': 1
        }
    )
    result = response.json()['result']
    return int(result, 16)

version = get_protocol_version()
print(f'Boba Network protocol version: {version}')

# eth_protocolVersion - Boba Network RPC Method
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
print(f'Boba Network protocol version: {w3.eth.protocol_version}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "eth_protocolVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Boba Network protocol version: %s\n", result)
}
```

## Common Use Cases

### 1. Node Compatibility Check

Verify protocol version before enabling features:

```javascript
async function checkCompatibility(provider, minVersion) {
  const result = await provider.send('eth_protocolVersion', []);
  const version = parseInt(result, 16);

  if (version >= minVersion) {
    console.log(`Node supports required protocol version ${minVersion}`);
    return true;
  } else {
    console.warn(`Node protocol version ${version} is below required ${minVersion}`);
    return false;
  }
}
```

### 2. Multi-Node Version Audit

Check protocol consistency across a fleet of Boba Network nodes:

```javascript
async function auditNodeVersions(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new JsonRpcProvider(endpoint);
      const [protocolVersion, clientVersion] = await Promise.all([
        provider.send('eth_protocolVersion', []),
        provider.send('web3_clientVersion', [])
      ]);
      return {
        endpoint,
        protocolVersion: parseInt(protocolVersion, 16),
        clientVersion
      };
    })
  );

  const versions = new Set(results.map(r => r.protocolVersion));
  if (versions.size > 1) {
    console.warn('Protocol version mismatch detected across nodes');
  }

  return results;
}
```

### 3. Feature Detection

Enable features based on the protocol version:

```javascript
async function getNodeCapabilities(provider) {
  try {
    const version = parseInt(await provider.send('eth_protocolVersion', []), 16);

    return {
      protocolVersion: version,
      supportsEIP1559: version >= 65,
      supportsSnapSync: version >= 66
    };
  } catch {
    // Some clients (e.g., post-Merge) may not support this method
    return { protocolVersion: null, supportsEIP1559: true, supportsSnapSync: true };
  }
}
```

zing - retry after delay |
\| -32005 | Rate limit exceeded | Reduce request frequency |

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Get the client software version string
- [`net_version`](https://www.dwellir.com/docs/boba-network/net_version) - Get the network ID
- [`eth_chainId`](https://www.dwellir.com/docs/boba-network/eth_chainId) - Get the chain ID (EIP-155)

---

## eth_sendRawTransaction - Boba Network RPC Method

Submits a pre-signed transaction for broadcast to Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

The `eth_sendRawTransaction` method serves these key scenarios for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Submit pre-signed transactions** - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- **Broadcast from offline signers** - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Boba Network
- **Resubmit stuck transactions** - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- **Deploy contracts programmatically** - Submit contract creation transactions containing compiled bytecode and constructor arguments

## Common Use Cases

### 1. Sign and Send an ETH Transfer

Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.

```javascript
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function sendNativeTransfer(to, amountInEth) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(amountInEth)
  });

  console.log('Transaction submitted:', tx.hash);

  const receipt = await tx.wait();
  console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
  return receipt;
}

const receipt = await sendNativeTransfer('0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', '0.01');
```

### 2. Resubmit a Stuck Transaction

If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Boba Network mempool.

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
  const pendingTx = await provider.getTransaction(originalTxHash);
  if (!pendingTx) throw new Error('Transaction not found');

  const feeData = await provider.getFeeData();

  const replacementTx = {
    to: pendingTx.to,
    value: pendingTx.value,
    data: pendingTx.data,
    nonce: pendingTx.nonce,
    maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
    gasLimit: pendingTx.gasLimit
  };

  const tx = await wallet.sendTransaction(replacementTx);
  console.log('Replacement transaction:', tx.hash);
  return tx;
}

const newTx = await speedUpTransaction('0x...');
```

### 3. Deploy a Compiled Contract

Submit a contract deployment transaction containing the compiled bytecode. The `to` field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.

```javascript
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);

const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';

async function deployContract(constructorArgs) {
  const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
  const contract = await factory.deploy(...constructorArgs);

  console.log('Deployment tx:', contract.deploymentTransaction().hash);

  await contract.waitForDeployment();
  console.log('Contract deployed at:', await contract.getAddress());
  return contract;
}

const contract = await deployContract(['MyToken', 1000000]);
```

## Best Practices

- Always sign transactions client-side: never send private keys to any RPC endpoint, including <https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY>
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from `eth_getTransactionCount` with `"pending"` tag before each signing
- The return value is a transaction hash: use `eth_getTransactionReceipt` to poll for confirmation status
- Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use `eth_estimateGas` to set an appropriate gas limit before signing and sending

## Request Parameters

- `signedTransactionData` (`DATA, required`): The signed transaction data (RLP encoded)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": ["0xf86c..."],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): 32-byte transaction hash

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x..."
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xf86c808504a817c80082520894..."],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

// Send native tokens
async function sendTransaction(to, value) {
  const tx = await wallet.sendTransaction({
    to: to,
    value: parseEther(value)
  });

  console.log('Transaction hash:', tx.hash);

  // Wait for confirmation
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);

  return receipt;
}

// Send to contract
async function sendContractTransaction(contract, method, args, value = '0') {
  const tx = await contract[method](https://www.dwellir.com/docs/boba-network/...args, {
    value: parseEther(value)
  });

  return await tx.wait();
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

def send_transaction(private_key, to, value_in_ether):
    account = w3.eth.account.from_key(private_key)

# eth_sendRawTransaction - Boba Network RPC Method
    tx = {
        'nonce': w3.eth.get_transaction_count(account.address),
        'to': to,
        'value': w3.to_wei(value_in_ether, 'ether'),
        'gas': 21000,
        'gasPrice': w3.eth.gas_price,
        'chainId': w3.eth.chain_id
    }

    # Sign transaction
    signed_tx = account.sign_transaction(tx)

    # Send transaction
    tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f'Transaction hash: {tx_hash.hex()}')

    # Wait for confirmation
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f'Confirmed in block: {receipt["blockNumber"]}')

    return receipt
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

    nonce, _ := client.PendingNonceAt(context.Background(), fromAddress)
    value := big.NewInt(1000000000000000000)
    gasLimit := uint64(21000)
    gasPrice, _ := client.SuggestGasPrice(context.Background())

    toAddress := common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")
    tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)

    chainID, _ := client.NetworkID(context.Background())
    signedTx, _ := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Transaction hash: %s\n", signedTx.Hash().Hex())
}
```

## Related Methods

- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas required
- [`eth_gasPrice`](https://www.dwellir.com/docs/boba-network/eth_gasPrice) - Get current gas price
- [`eth_getTransactionReceipt`](https://www.dwellir.com/docs/boba-network/eth_getTransactionReceipt) - Get transaction result

---

## eth_sendTransaction - Boba Network RPC Method

Creates and sends a new transaction from an unlocked account on Boba Network. The node signs the transaction server-side using the private key associated with the `from` address.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

> **Security Warning:** Public Dwellir endpoints do not manage unlocked accounts for you. On shared infrastructure, `eth_sendTransaction` commonly returns an unsupported-method response or an account-management error such as `unknown account`, depending on the client. For production applications, **sign transactions client-side** and use [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) instead.

## When to Use This Method

`eth_sendTransaction` is useful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access in development scenarios:

- **Local Development** - Send transactions quickly on local nodes (Hardhat, Anvil, Ganache) without managing private keys
- **Testing Workflows** - Rapidly prototype and test contract interactions on dev networks
- **Scripted Deployments** - Deploy contracts on private or permissioned networks with unlocked accounts

## Best Practices

- Requires an unlocked account on the node, which is a significant security risk in production
- Prefer `eth_sendRawTransaction` with client-side signed transactions for all production use cases
- Node-managed accounts are disabled on most public providers; this method is best for local development only

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the sending account (must be unlocked on the node)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `maxFeePerGas` (`QUANTITY, optional`): Maximum total fee per gas (EIP-1559 transactions)
- `maxPriorityFeePerGas` (`QUANTITY, optional`): Maximum priority fee per gas (EIP-1559 transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_sendTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`DATA (32 bytes), required`): The transaction hash, or zero hash if the transaction is not yet available

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_sendTransaction - Boba Network RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a"
    }],
    "id": 1
  }'
```

```javascript
// On a local dev node with unlocked accounts (e.g., Hardhat)
const response = await fetch('http://127.0.0.1:8545', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'eth_sendTransaction',
    params: [{
      from: '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
      to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
      gas: '0x76c0',
      gasPrice: '0x9184e72a000',
      value: '0x9184e72a'
    }],
    id: 1
  })
});

const { result: txHash } = await response.json();
console.log('Boba Network tx hash:', txHash);

// Recommended for production: client-side signing
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = await wallet.sendTransaction({
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1')
});
console.log('Boba Network tx hash:', tx.hash);
```

```python
import requests
from web3 import Web3

# Direct RPC call on a LOCAL dev node with unlocked accounts
response = requests.post(
    'http://127.0.0.1:8545',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_sendTransaction',
        'params': [{
            'from': '0x407d73d8a49eeb85d32cf465507dd71d507100c1',
            'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
            'gas': '0x76c0',
            'gasPrice': '0x9184e72a000',
            'value': '0x9184e72a'
        }],
        'id': 1
    }
)
tx_hash = response.json()['result']
print(f'Boba Network tx hash: {tx_hash}')

# Recommended for production: client-side signing
w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))
account = w3.eth.account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Boba Network tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing with eth_sendRawTransaction
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, gasPrice, nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    err = client.SendTransaction(context.Background(), signedTx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Boba Network tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Local Development with Hardhat

Send transactions using Hardhat's pre-funded unlocked accounts:

```javascript
async function devTransfer(provider, from, to, value) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    to,
    value: '0x' + value.toString(16),
    gas: '0x5208' // 21000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Transfer confirmed in block ${receipt.blockNumber}`);
  return receipt;
}
```

### 2. Contract Deployment on Dev Network

Deploy contracts without managing private keys locally:

```javascript
async function deployContract(provider, from, bytecode) {
  const txHash = await provider.send('eth_sendTransaction', [{
    from,
    data: bytecode,
    gas: '0x4C4B40' // 5,000,000
  }]);

  const receipt = await provider.waitForTransaction(txHash);
  console.log(`Contract deployed at: ${receipt.contractAddress}`);
  return receipt.contractAddress;
}
```

### 3. Batch Transfers in Testing

Send multiple test transactions on Boba Network dev networks:

```javascript
async function batchTransfer(provider, from, recipients) {
  const hashes = [];

  for (const { to, value } of recipients) {
    const hash = await provider.send('eth_sendTransaction', [{
      from,
      to,
      value: '0x' + value.toString(16),
      gas: '0x5208'
    }]);
    hashes.push(hash);
  }

  return hashes;
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Broadcast a pre-signed transaction (recommended for production)
- [`eth_signTransaction`](https://www.dwellir.com/docs/boba-network/eth_signTransaction) - Sign without sending (requires unlocked account)
- [`eth_estimateGas`](https://www.dwellir.com/docs/boba-network/eth_estimateGas) - Estimate gas cost before sending

---

## eth_signTransaction - Boba Network RPC Method

Signs a transaction with the private key of the specified account on Boba Network without submitting it to the network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

> **Security Warning:** Public Dwellir endpoints do not keep unlocked signers. On shared infrastructure, `eth_signTransaction` commonly returns a deprecation, unsupported-method, or account-management error instead of a signed payload. For production use, **sign transactions client-side** using libraries like [ethers.js](https://docs.ethers.org/) or [web3.py](https://github.com/ethereum/web3.py), then broadcast with [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction).

## When to Use This Method

`eth_signTransaction` is relevant for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access in limited scenarios:

- **Understanding the Signing Flow** - Learn how transaction signing works before implementing client-side signing
- **Local Development** - Sign transactions on a local dev node (Hardhat, Anvil, Ganache) where accounts are unlocked
- **Offline Signing Workflows** - Generate signed transaction payloads for later broadcast

## Best Practices

- Sign transactions client-side using wallet libraries for production applications
- Never expose private keys to node endpoints; use `eth_sendRawTransaction` with pre-signed transactions
- Use `eth_signTransaction` only in test environments or for air-gapped signing validation

## Request Parameters

- `from` (`DATA (20 bytes), required`): Address of the account to sign with (must be unlocked)
- `to` (`DATA (20 bytes), optional`): Recipient address (omit for contract creation)
- `gas` (`QUANTITY, optional`): Gas limit for the transaction (default: 90000)
- `gasPrice` (`QUANTITY, optional`): Gas price in wei (legacy transactions)
- `value` (`QUANTITY, optional`): Value to send in wei
- `data` (`DATA, optional`): Compiled contract code or encoded method call
- `nonce` (`QUANTITY, optional`): Transaction nonce (defaults to eth_getTransactionCount)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_signTransaction",
  "params": [
    {
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "data": "0x",
      "nonce": "0x0"
    }
  ],
  "id": 1
}
```

## Response Fields

- `raw` (`DATA, required`): The RLP-encoded signed transaction, ready for eth_sendRawTransaction
- `tx` (`Object, required`): The transaction object including v, r, s signature fields

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "raw": "0xf86c808609184e72a0008276c094a94f5374fce5edbc8e2a8697c15331677e6ebf0b849184e72a801ba0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
    "tx": {
      "nonce": "0x0",
      "gasPrice": "0x9184e72a000",
      "gas": "0x76c0",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "value": "0x9184e72a",
      "input": "0x",
      "v": "0x1b",
      "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7571d7c13e32e8ce2c6c8a...",
      "s": "0x3d850e0b25e3c7a3e4da3a7c1b1a4e3b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e..."
    }
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# eth_signTransaction - Boba Network RPC Method
curl -X POST http://127.0.0.1:8545 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_signTransaction",
    "params": [{
      "from": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
      "to": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
      "gas": "0x76c0",
      "gasPrice": "0x9184e72a000",
      "value": "0x9184e72a",
      "nonce": "0x0"
    }],
    "id": 1
  }'
```

```javascript
// Recommended: client-side signing with ethers.js
import { Wallet, JsonRpcProvider, parseEther } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider);

const tx = {
  to: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
  value: parseEther('0.1'),
  gasLimit: 21000
};

// Sign without sending
const signedTx = await wallet.signTransaction(tx);
console.log('Signed Boba Network tx:', signedTx);

// Send later with eth_sendRawTransaction
const receipt = await provider.broadcastTransaction(signedTx);
console.log('Tx hash:', receipt.hash);
```

```python
from web3 import Web3
from eth_account import Account

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Recommended: client-side signing
account = Account.from_key('YOUR_PRIVATE_KEY')

tx = {
    'to': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b',
    'value': w3.to_wei(0.1, 'ether'),
    'gas': 21000,
    'gasPrice': w3.eth.gas_price,
    'nonce': w3.eth.get_transaction_count(account.address),
    'chainId': w3.eth.chain_id
}

signed = account.sign_transaction(tx)
print(f'Signed Boba Network tx: {signed.raw_transaction.hex()}')

# Send later
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f'Tx hash: {tx_hash.hex()}')
```

```go
package main

import (
    "context"
    "crypto/ecdsa"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Recommended: client-side signing
    privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY_HEX")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public().(*ecdsa.PublicKey)
    fromAddress := crypto.PubkeyToAddress(*publicKey)

    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    toAddress := common.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")
    tx := types.NewTransaction(nonce, toAddress, big.NewInt(100000000), 21000, big.NewInt(10000000000), nil)

    chainID, err := client.ChainID(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Signed Boba Network tx hash: %s\n", signedTx.Hash().Hex())
}
```

## Common Use Cases

### 1. Offline Transaction Signing

Sign transactions on an air-gapped machine for later broadcast:

```javascript
import { Wallet } from 'ethers';

async function createSignedTransaction(privateKey, to, value, { chainId, nonce, gasLimit }) {
  const wallet = new Wallet(privateKey);
  const tx = {
    to,
    value,
    gasLimit,
    nonce,
    chainId,
  };

  const signedTx = await wallet.signTransaction(tx);
  // Store signedTx and broadcast from an online machine
  return signedTx;
}
```

### 2. Batch Transaction Preparation

Pre-sign multiple transactions for sequential submission on Boba Network:

```javascript
async function prepareBatch(wallet, transactions) {
  const signed = [];

  for (let i = 0; i < transactions.length; i++) {
    const tx = {
      ...transactions[i],
      nonce: baseNonce + i
    };
    signed.push(await wallet.signTransaction(tx));
  }

  return signed; // Submit via eth_sendRawTransaction in order
}
```

## Related Methods

- [`eth_sendRawTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendRawTransaction) - Broadcast a signed transaction
- [`eth_sendTransaction`](https://www.dwellir.com/docs/boba-network/eth_sendTransaction) - Sign and send in one step (requires unlocked account)
- [`eth_accounts`](https://www.dwellir.com/docs/boba-network/eth_accounts) - List accounts available for signing

---

## eth_syncing - Boba Network RPC Method

# eth_syncing - Boba Network RPC Method

Returns the sync status of your Boba Network node - either `false` when fully synced, or an object describing the sync progress.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_syncing` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Node Health Checks** - Verify your node is fully synced before processing transactions
- **Sync Progress Monitoring** - Track how far behind your node is during initial sync or after downtime
- **Load Balancer Routing** - Route requests only to fully synced nodes in multi-node setups
- **dApp Reliability** - Display sync warnings to users when data may be stale

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}
```

## Response Fields

- `startingBlock` (`QUANTITY, required`): Block number where sync started
- `currentBlock` (`QUANTITY, required`): Current block being processed
- `highestBlock` (`QUANTITY, required`): Estimated highest block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_syncing",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const syncing = await provider.send('eth_syncing', []);

if (syncing === false) {
  console.log('Boba Network node is fully synced');
} else {
  const current = parseInt(syncing.currentBlock, 16);
  const highest = parseInt(syncing.highestBlock, 16);
  const progress = ((current / highest) * 100).toFixed(2);
  console.log(`Syncing: ${progress}% (block ${current} / ${highest})`);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

sync_status = w3.eth.syncing

if sync_status is False:
    print('Boba Network node is fully synced')
else:
    current = sync_status['currentBlock']
    highest = sync_status['highestBlock']
    progress = (current / highest) * 100
    print(f'Syncing: {progress:.2f}% (block {current} / {highest})')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    progress, err := client.SyncProgress(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    if progress == nil {
        fmt.Println("Boba Network node is fully synced")
    } else {
        fmt.Printf("Syncing: block %d / %d\n", progress.CurrentBlock, progress.HighestBlock)
    }
}
```

## Common Use Cases

### 1. Node Health Monitor

Continuously check sync status and alert on issues:

```javascript
async function monitorNodeHealth(provider, interval = 30000) {
  setInterval(async () => {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      const blockNumber = await provider.getBlockNumber();
      const block = await provider.getBlock(blockNumber);
      const age = Date.now() / 1000 - block.timestamp;

      if (age > 60) {
        console.warn(`Node synced but block is ${age.toFixed(0)}s old`);
      } else {
        console.log(`Healthy - block ${blockNumber}`);
      }
    } else {
      const current = parseInt(syncing.currentBlock, 16);
      const highest = parseInt(syncing.highestBlock, 16);
      console.warn(`Syncing: ${highest - current} blocks behind`);
    }
  }, interval);
}
```

### 2. Wait for Sync Before Processing

Block application startup until the node is ready:

```javascript
async function waitForSync(provider, pollInterval = 5000) {
  while (true) {
    const syncing = await provider.send('eth_syncing', []);

    if (syncing === false) {
      console.log('Node synced - ready to process transactions');
      return;
    }

    const current = parseInt(syncing.currentBlock, 16);
    const highest = parseInt(syncing.highestBlock, 16);
    console.log(`Waiting for sync: ${highest - current} blocks remaining...`);
    await new Promise(r => setTimeout(r, pollInterval));
  }
}
```

## Best Practices

- Poll `eth_syncing` at application startup and block all transaction operations until `false` is returned
- Check both the sync status AND the latest block timestamp age -- a node can report synced but still have stale data
- In multi-node setups, route read requests to synced nodes and avoid sending transactions to nodes still catching up
- For long-running services, implement a health check that raises alerts if the sync gap exceeds a threshold
- Some client implementations return a sync object with additional fields like `knownStates` and `pulledStates` during state sync

## Related Methods

- [`eth_blockNumber`](https://www.dwellir.com/docs/boba-network/eth_blockNumber) - Get current block height
- [`net_peerCount`](https://www.dwellir.com/docs/boba-network/net_peerCount) - Check peer connections
- [`net_listening`](https://www.dwellir.com/docs/boba-network/net_listening) - Verify node is accepting connections
- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Get node client info

---

## eth_uninstallFilter - Boba Network RPC Method

Removes a filter on Boba Network that was previously created with `eth_newFilter`, `eth_newBlockFilter`, or `eth_newPendingTransactionFilter`. Should always be called when a filter is no longer needed to free server-side resources.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`eth_uninstallFilter` is important for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Resource Cleanup** - Remove filters you no longer need to free memory and processing on the node
- **Post-Monitoring Teardown** - Clean up after event monitoring sessions end or when switching to different filter criteria
- **Preventing Stale Filters** - Proactively remove filters before they auto-expire to maintain clean state
- **Connection Management** - Uninstall filters before disconnecting to avoid orphaned server-side resources

## Best Practices

- Always uninstall filters in a `finally` block to guarantee cleanup even when errors occur
- Filters expire automatically after a period of inactivity, but explicit uninstallation is more reliable
- Uninstall all active filters when your application shuts down to avoid leaving orphaned resources

## Request Parameters

- `filterId` (`QUANTITY, required`): The ID of the filter to remove (returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_uninstallFilter",
  "params": ["0x1"],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the filter was found and successfully removed, false if the filter ID was not found (already removed or expired)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_uninstallFilter",
    "params": ["0x1"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Create a filter first
const filterId = await provider.send('eth_newBlockFilter', []);
console.log('Created filter:', filterId);

// ... poll with eth_getFilterChanges ...

// Remove the filter when done
const removed = await provider.send('eth_uninstallFilter', [filterId]);
console.log('Filter removed:', removed);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

# eth_uninstallFilter - Boba Network RPC Method
filter_id = w3.eth.filter('latest').filter_id

# ... poll for changes ...

# Remove the filter when done
removed = w3.provider.make_request('eth_uninstallFilter', [filter_id])
print(f'Filter removed: {removed["result"]}')

# Using requests directly
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'eth_uninstallFilter',
        'params': ['0x1'],
        'id': 1
    }
)
print(f'Removed: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Create a block filter
    var filterId string
    err = client.CallContext(context.Background(), &filterId, "eth_newBlockFilter")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Created filter: %s\n", filterId)

    // Remove the filter
    var removed bool
    err = client.CallContext(context.Background(), &removed, "eth_uninstallFilter", filterId)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Filter removed: %t\n", removed)
}
```

## Common Use Cases

### 1. Filter Lifecycle Manager

Create a managed filter that automatically cleans up:

```javascript
class ManagedFilter {
  constructor(provider) {
    this.provider = provider;
    this.filterId = null;
    this.polling = false;
  }

  async createLogFilter(filterOptions) {
    this.filterId = await this.provider.send('eth_newFilter', [filterOptions]);
    console.log('Filter created:', this.filterId);
    return this.filterId;
  }

  async createBlockFilter() {
    this.filterId = await this.provider.send('eth_newBlockFilter', []);
    return this.filterId;
  }

  async poll() {
    if (!this.filterId) throw new Error('No active filter');
    return await this.provider.send('eth_getFilterChanges', [this.filterId]);
  }

  async destroy() {
    if (this.filterId) {
      const removed = await this.provider.send('eth_uninstallFilter', [this.filterId]);
      console.log(`Filter ${this.filterId} removed: ${removed}`);
      this.filterId = null;
      return removed;
    }
    return false;
  }
}

// Usage
const filter = new ManagedFilter(provider);
await filter.createBlockFilter();

try {
  const changes = await filter.poll();
  console.log('New blocks:', changes);
} finally {
  await filter.destroy();
}
```

### 2. Event Monitor with Cleanup

Monitor events for a limited duration, then clean up all filters:

```javascript
async function monitorEvents(provider, contractAddress, topics, duration = 60000) {
  const filterId = await provider.send('eth_newFilter', [{
    address: contractAddress,
    topics
  }]);

  const allEvents = [];
  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    if (changes.length > 0) {
      allEvents.push(...changes);
      console.log(`Received ${changes.length} new events`);
    }
  }, 2000);

  // Stop monitoring after the specified duration
  await new Promise(r => setTimeout(r, duration));
  clearInterval(interval);

  // Always clean up the filter
  const removed = await provider.send('eth_uninstallFilter', [filterId]);
  console.log(`Monitoring complete. Filter removed: ${removed}. Total events: ${allEvents.length}`);

  return allEvents;
}
```

### 3. Bulk Filter Cleanup

Remove all tracked filters during application shutdown:

```python
import requests

class FilterRegistry:
    def __init__(self, rpc_url):
        self.rpc_url = rpc_url
        self.active_filters = []

    def create_filter(self, filter_type='block'):
        method = {
            'block': 'eth_newBlockFilter',
            'pending': 'eth_newPendingTransactionFilter',
        }.get(filter_type, 'eth_newBlockFilter')

        response = requests.post(
            self.rpc_url,
            json={'jsonrpc': '2.0', 'method': method, 'params': [], 'id': 1}
        )
        filter_id = response.json()['result']
        self.active_filters.append(filter_id)
        return filter_id

    def cleanup_all(self):
        removed = 0
        for filter_id in self.active_filters:
            response = requests.post(
                self.rpc_url,
                json={'jsonrpc': '2.0', 'method': 'eth_uninstallFilter', 'params': [filter_id], 'id': 1}
            )
            if response.json().get('result'):
                removed += 1
        print(f'Cleaned up {removed}/{len(self.active_filters)} filters')
        self.active_filters.clear()
```

## Related Methods

- [`eth_newFilter`](https://www.dwellir.com/docs/boba-network/eth_newFilter) - Create a log event filter
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/boba-network/eth_newBlockFilter) - Create a new block filter
- [`eth_newPendingTransactionFilter`](https://www.dwellir.com/docs/boba-network/eth_newPendingTransactionFilter) - Create a pending transaction filter
- [`eth_getFilterChanges`](https://www.dwellir.com/docs/boba-network/eth_getFilterChanges) - Poll a filter for new results
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/boba-network/eth_getFilterLogs) - Get all logs matching a filter

---

## net_listening - Boba Network RPC Method

Checks whether the connected Boba Network client reports that its P2P networking layer is listening for peers. Depending on the client behind the endpoint, this call may return `true`, `false`, or an unsupported-method error.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`net_listening` is useful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **P2P Diagnostics** - Check whether the connected client exposes peer-listening state at all
- **Client Capability Checks** - Distinguish between boolean responses and unsupported-method errors across different node clients
- **Peer-Network Troubleshooting** - Combine with `net_peerCount` and `eth_syncing` when investigating peer discovery or sync issues
- **Operational Audits** - Confirm what the endpoint reports before wiring it into monitoring or dashboards

## Best Practices

- Returns a boolean value only; combine with `net_peerCount` and `eth_syncing` for a comprehensive health check
- A `false` return indicates the node is not accepting network connections
- Some node clients may return an unsupported-method error instead of a boolean; handle both cases

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_listening",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Boolean, required`): true if the client reports that it is listening for peers, false otherwise

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_listening",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

try {
  const listening = await provider.send('net_listening', []);
  console.log('Boba Network node listening:', listening);
} catch (error) {
  console.log('net_listening unsupported:', error.message);
}

// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_listening',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('net_listening unsupported:', payload.error.message);
} else {
  console.log('Listening:', payload.result);
}
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

try:
    listening = w3.net.listening
    print(f'Boba Network node listening: {listening}')
except Exception as exc:
    print(f'net_listening unsupported: {exc}')

# net_listening - Boba Network RPC Method
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_listening',
        'params': [],
        'id': 1
    }
)
payload = response.json()
if 'error' in payload:
    print(f"net_listening unsupported: {payload['error']['message']}")
else:
    print(f"Listening: {payload['result']}")
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var listening bool
    err = client.CallContext(context.Background(), &listening, "net_listening")
    if err != nil {
        log.Println("net_listening unsupported:", err)
        return
    }

    fmt.Printf("Boba Network node listening: %t\n", listening)
}
```

## Common Use Cases

### 1. Capability-Aware Peer Diagnostic

Combine `net_listening` with other status methods, but treat unsupported responses as normal:

```javascript
async function getPeerDiagnostic(provider) {
  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  let listening = { supported: false };
  try {
    listening = { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    listening = { supported: false, message: error.message };
  }

  const peerCount = parseInt(peerCountHex, 16);
  return {
    listening,
    peerCount,
    synced: syncing === false
  };
}
```

### 2. Fallback-Friendly Check

```javascript
async function readListeningStatus(provider) {
  try {
    return { supported: true, value: await provider.send('net_listening', []) };
  } catch (error) {
    return { supported: false, message: error.message };
  }
}
```

### 3. Multi-Node Fleet Monitor

Check which endpoints expose `net_listening` and what they report:

```python
import requests
from concurrent.futures import ThreadPoolExecutor

def check_fleet_listening(endpoints):
    def check_node(endpoint):
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_listening', 'params': [], 'id': 1},
                timeout=5
            )
            payload = response.json()
            if 'error' in payload:
                return {'endpoint': endpoint, 'supported': False, 'error': payload['error']['message']}
            return {'endpoint': endpoint, 'supported': True, 'listening': payload.get('result', False)}
        except Exception as e:
            return {'endpoint': endpoint, 'supported': False, 'error': str(e)}

    with ThreadPoolExecutor(max_workers=10) as executor:
        results = list(executor.map(check_node, endpoints))

    supported = [r for r in results if r.get('supported')]
    unsupported = [r for r in results if not r.get('supported')]

    print(f'{len(supported)}/{len(results)} endpoints expose net_listening')
    for node in unsupported:
        print(f'  UNSUPPORTED: {node["endpoint"]} - {node.get("error", "not available")}')

    return results
```

## Related Methods

- [`net_peerCount`](https://www.dwellir.com/docs/boba-network/net_peerCount) - Get number of connected peers
- [`net_version`](https://www.dwellir.com/docs/boba-network/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Get node client info

---

## net_peerCount - Boba Network RPC Method

Returns the number of peers currently connected to your Boba Network node.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`net_peerCount` is important for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Network Health Monitoring** - Verify your node has sufficient peer connections for reliable data propagation
- **Peer Discovery Verification** - Confirm that peer discovery is working after node startup or network changes
- **Load Balancer Decisions** - Route traffic to well-connected nodes with healthy peer counts
- **Troubleshooting Connectivity** - Diagnose networking issues when a node returns stale or missing data

## Best Practices

- Low peer count may indicate connectivity or firewall issues; investigate if peers drop unexpectedly
- Minimum healthy peer count varies by network; establish baselines for your specific Boba Network deployment
- Combine with `eth_syncing` for a complete picture of node health and readiness

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_peerCount",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): Hexadecimal string representing the number of connected peers

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x19"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_peerCount does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_peerCount",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const peerCountHex = await provider.send('net_peerCount', []);
const peerCount = parseInt(peerCountHex, 16);
console.log(`Boba Network peers: ${peerCount}`);

// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_peerCount',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Peers:', parseInt(result, 16));
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

peer_count = w3.net.peer_count
print(f'Boba Network peers: {peer_count}')

# net_peerCount - Boba Network RPC Method
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_peerCount',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Peers: {int(result, 16)}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var result string
    err = client.CallContext(context.Background(), &result, "net_peerCount")
    if err != nil {
        log.Fatal(err)
    }

    peerCount := new(big.Int)
    peerCount.SetString(result[2:], 16)
    fmt.Printf("Boba Network peers: %s\n", peerCount.String())
}
```

## Common Use Cases

### 1. Network Health Monitor

Continuously check peer count and alert when it drops below a threshold:

```javascript
async function monitorPeers(provider, minPeers = 3, interval = 30000) {
  setInterval(async () => {
    const peerCountHex = await provider.send('net_peerCount', []);
    const peerCount = parseInt(peerCountHex, 16);

    if (peerCount === 0) {
      console.error('CRITICAL: Node has no peers - network isolated');
    } else if (peerCount < minPeers) {
      console.warn(`LOW PEERS: ${peerCount} connected (minimum: ${minPeers})`);
    } else {
      console.log(`Healthy - ${peerCount} peers connected`);
    }
  }, interval);
}
```

### 2. Node Readiness Check

Verify a node has enough peers before routing traffic to it:

```javascript
async function isNodeReady(rpcUrl, minPeers = 5) {
  const provider = new JsonRpcProvider(rpcUrl);

  const [peerCountHex, syncing] = await Promise.all([
    provider.send('net_peerCount', []),
    provider.send('eth_syncing', [])
  ]);

  const peerCount = parseInt(peerCountHex, 16);
  const isSynced = syncing === false;
  const hasEnoughPeers = peerCount >= minPeers;

  return {
    ready: isSynced && hasEnoughPeers,
    peerCount,
    syncing: !isSynced
  };
}
```

### 3. Multi-Node Fleet Dashboard

Aggregate peer counts across a fleet of Boba Network nodes:

```python
import requests

def check_fleet_peers(endpoints):
    results = []
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'net_peerCount', 'params': [], 'id': 1},
                timeout=5
            )
            peers = int(response.json()['result'], 16)
            results.append({'endpoint': endpoint, 'peers': peers, 'healthy': peers > 0})
        except Exception as e:
            results.append({'endpoint': endpoint, 'peers': 0, 'healthy': False, 'error': str(e)})
    return results
```

## Related Methods

- [`net_listening`](https://www.dwellir.com/docs/boba-network/net_listening) - Check if node is accepting connections
- [`net_version`](https://www.dwellir.com/docs/boba-network/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check node sync progress
- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Get node client info

---

## net_version - Boba Network RPC Method

Returns the current network ID on Boba Network as a decimal string. The network ID identifies which network the node is connected to.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`net_version` is essential for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Endpoint Identification** - Confirm your application is connected to the expected Boba Network network
- **Multi-Chain App Routing** - Dynamically detect which network an RPC endpoint serves and route logic accordingly
- **Connection Validation** - Perform a quick sanity check during node or provider initialization

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The current network ID as a decimal string (e.g., "1" for Ethereum mainnet, "5" for Goerli)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "1"
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "The method net_version does not exist/is not available"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "net_version",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const networkId = await provider.send('net_version', []);
console.log('Boba Network network ID:', networkId);

// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'net_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Network ID:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

network_id = w3.net.version
print(f'Boba Network network ID: {network_id}')

# net_version - Boba Network RPC Method
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'net_version',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Network ID: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var networkId string
    err = client.CallContext(context.Background(), &networkId, "net_version")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Boba Network network ID: %s\n", networkId)
}
```

## Common Use Cases

### 1. Multi-Chain Connection Validator

Verify your application connects to the expected network before processing any transactions:

```javascript
const EXPECTED_NETWORKS = {
  '1': 'Ethereum Mainnet',
  '137': 'Polygon',
  '42161': 'Arbitrum One',
  '10': 'Optimism',
};

async function validateNetwork(provider, expectedNetworkId) {
  const networkId = await provider.send('net_version', []);

  if (networkId !== expectedNetworkId) {
    const actual = EXPECTED_NETWORKS[networkId] || `Unknown (${networkId})`;
    const expected = EXPECTED_NETWORKS[expectedNetworkId] || expectedNetworkId;
    throw new Error(`Wrong network: connected to ${actual}, expected ${expected}`);
  }

  console.log(`Connected to ${EXPECTED_NETWORKS[networkId]}`);
  return networkId;
}
```

### 2. Dynamic Chain Router

Route application logic based on the detected network:

```javascript
async function createChainRouter(rpcUrl) {
  const provider = new JsonRpcProvider(rpcUrl);
  const networkId = await provider.send('net_version', []);

  const config = {
    '1': { explorer: 'https://etherscan.io', confirmations: 12 },
    '137': { explorer: 'https://polygonscan.com', confirmations: 128 },
    '42161': { explorer: 'https://arbiscan.io', confirmations: 1 },
  };

  if (!config[networkId]) {
    throw new Error(`Unsupported network ID: ${networkId}`);
  }

  return { provider, networkId, ...config[networkId] };
}
```

### 3. Network ID vs Chain ID Comparison

Compare `net_version` with `eth_chainId` when you need both endpoint identity and signing context:

```python
from web3 import Web3

def verify_chain_identity(rpc_url):
    w3 = Web3(Web3.HTTPProvider(rpc_url))

    network_id = int(w3.net.version)
    chain_id = w3.eth.chain_id

    if network_id != chain_id:
        print(f'Network ID ({network_id}) differs from chain ID ({chain_id})')
    else:
        print(f'Network and chain ID match: {chain_id}')

    print('Use eth_chainId as the signing source of truth')

    return {'network_id': network_id, 'chain_id': chain_id}
```

## Best Practices

- Use `eth_chainId` for transaction signing (EIP-155 replay protection) -- `net_version` is for network identification only
- Cache the network ID at startup -- it does not change during a session
- Some L2 and sidechain networks share the same network ID as their L1 -- always combine with `eth_chainId` for unambiguous identification
- For multi-chain dApps, maintain a mapping of network IDs to chain-specific contract addresses and RPC endpoints
- The `net_*` namespace may be disabled on some node configurations -- handle the -32601 error gracefully

## Related Methods

- [`eth_chainId`](https://www.dwellir.com/docs/boba-network/eth_chainId) - Get the EIP-155 chain ID (preferred for transaction signing)
- [`net_listening`](https://www.dwellir.com/docs/boba-network/net_listening) - Check whether the client reports peer-listening state
- [`net_peerCount`](https://www.dwellir.com/docs/boba-network/net_peerCount) - Get number of connected peers
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check node sync progress

---

## rpc_modules - Boba Network RPC Method

# rpc_modules - Boba Network RPC Method

Returns the enabled JSON-RPC namespaces exposed by the connected Boba Network endpoint together with their version strings.

> **Non-standard method.** `rpc_modules` is a client-introspection RPC that is commonly available on Geth-compatible stacks, but it is not part of the core Ethereum Execution API method set. Availability varies by client and operator policy.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`rpc_modules` is useful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Capability Discovery** - Detect whether namespaces like `debug`, `trace`, `txpool`, or `erigon` are exposed before attempting those calls
- **Client Diagnostics** - Verify what the serving node has enabled when debugging environment-specific issues
- **Infrastructure Audits** - Compare public and private endpoints to confirm which RPC surfaces are intentionally exposed
- **Runtime Feature Gating** - Adjust tooling behavior dynamically based on the actual namespaces available on a node

## Best Practices

- Call at startup to determine which features are available on a node
- Module availability varies by node client and provider configuration
- Use to gate feature access in applications before attempting unsupported calls
- This is a non-standard method; some endpoints may not expose it

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_modules",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Object, required`): Object whose keys are enabled namespaces and whose values are version strings

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "eth": "1.0",
    "net": "1.0",
    "web3": "1.0",
    "rpc": "1.0",
    "debug": "1.0",
    "trace": "1.0",
    "txpool": "1.0"
  }
}
```

## Error Responses

### Error Response

- Code: `-32601`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_modules",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const modules = await provider.send('rpc_modules', []);
console.log('Namespaces:', Object.keys(modules));

if (modules.debug) {
  console.log('Debug RPC is enabled');
}
```

```python
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'rpc_modules',
        'params': [],
        'id': 1,
    },
)

modules = response.json()['result']
print('Namespaces:', sorted(modules.keys()))
print('Has trace:', 'trace' in modules)
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "sort"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var modules map[string]string
    err = client.CallContext(context.Background(), &modules, "rpc_modules")
    if err != nil {
        log.Fatal(err)
    }

    names := make([]string, 0, len(modules))
    for name := range modules {
        names = append(names, name)
    }
    sort.Strings(names)
    fmt.Printf("Namespaces: %v\n", names)
}
```

## Related Methods

- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Inspect the client software version string
- [`debug_traceTransaction`](https://www.dwellir.com/docs/boba-network/debug_traceTransaction) - Debug namespace example
- `trace_transaction` - Trace namespace example

---

## web3_clientVersion - Boba Network RPC Method

Returns the current client software version string for your Boba Network node, including the client name, version number, OS, and runtime.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

## When to Use This Method

`web3_clientVersion` is valuable for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Client Compatibility Checks** - Verify the node runs a client version that supports the RPC methods your application needs
- **Fleet Version Monitoring** - Track client versions across a multi-node infrastructure to coordinate upgrades
- **Debugging Client-Specific Behavior** - Identify which client (Geth, Erigon, Nethermind, Besu, etc.) is serving requests when behavior differs
- **Security Auditing** - Detect nodes running outdated versions with known vulnerabilities

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): Client version string, typically in the format ClientName/vX.Y.Z/OS/Runtime

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Geth/v1.13.5-stable/linux-amd64/go1.21.4"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_clientVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

const clientVersion = await provider.send('web3_clientVersion', []);
console.log('Boba Network client:', clientVersion);

// Using fetch
const response = await fetch('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'web3_clientVersion',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Client version:', result);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

client_version = w3.client_version
print(f'Boba Network client: {client_version}')

# web3_clientVersion - Boba Network RPC Method
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_clientVersion',
        'params': [],
        'id': 1
    }
)
result = response.json()['result']
print(f'Client version: {result}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    var clientVersion string
    err = client.CallContext(context.Background(), &clientVersion, "web3_clientVersion")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Boba Network client: %s\n", clientVersion)
}
```

## Common Use Cases

### 1. Client Version Parser

Parse the version string to extract structured information:

```javascript
function parseClientVersion(versionString) {
  const parts = versionString.split('/');

  return {
    client: parts[0],
    version: parts[1] || 'unknown',
    os: parts[2] || 'unknown',
    runtime: parts[3] || 'unknown'
  };
}

async function getNodeInfo(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const parsed = parseClientVersion(version);

  console.log(`Client: ${parsed.client}`);
  console.log(`Version: ${parsed.version}`);
  console.log(`OS: ${parsed.os}`);
  console.log(`Runtime: ${parsed.runtime}`);

  return parsed;
}
```

### 2. Fleet Version Audit

Check all nodes in a fleet and report version inconsistencies:

```python
import requests

def audit_fleet_versions(endpoints):
    versions = {}
    for endpoint in endpoints:
        try:
            response = requests.post(
                endpoint,
                json={'jsonrpc': '2.0', 'method': 'web3_clientVersion', 'params': [], 'id': 1},
                timeout=5
            )
            version = response.json()['result']
            versions[endpoint] = version
        except Exception as e:
            versions[endpoint] = f'ERROR: {e}'

    # Group by client version
    grouped = {}
    for endpoint, version in versions.items():
        grouped.setdefault(version, []).append(endpoint)

    for version, nodes in grouped.items():
        print(f'{version}: {len(nodes)} node(s)')

    if len(grouped) > 1:
        print('WARNING: Inconsistent versions detected across fleet')

    return versions
```

### 3. Feature Detection by Client

Adjust RPC behavior based on the detected client type:

```javascript
async function detectClientCapabilities(provider) {
  const version = await provider.send('web3_clientVersion', []);
  const client = version.split('/')[0].toLowerCase();

  const capabilities = {
    supportsDebugTrace: ['geth', 'erigon'].includes(client),
    supportsParityTrace: ['openethereum', 'nethermind', 'erigon'].includes(client),
    supportsEthSubscribe: true, // all modern clients
  };

  console.log(`Client "${client}" capabilities:`, capabilities);
  return capabilities;
}
```

## Best Practices

- Parse the client version string at startup and log it for debugging -- different clients may handle edge cases differently
- Maintain a fleet inventory that tracks client versions and flags nodes running outdated software
- When reporting issues to node providers, always include the `web3_clientVersion` output
- Some clients expose additional features based on version -- check version strings for feature gates (e.g., Erigon 3.x supports `ots_*` namespace)
- The version string format varies across clients -- avoid hardcoding assumptions about the delimiter or field count

## Related Methods

- [`net_version`](https://www.dwellir.com/docs/boba-network/net_version) - Get the network ID
- [`eth_syncing`](https://www.dwellir.com/docs/boba-network/eth_syncing) - Check node sync progress
- [`web3_sha3`](https://www.dwellir.com/docs/boba-network/web3_sha3) - Compute Keccak-256 hash via RPC
- [`net_peerCount`](https://www.dwellir.com/docs/boba-network/net_peerCount) - Get number of connected peers

---

## web3_sha3 - Boba Network RPC Method

Returns the Keccak-256 hash (not standard SHA3-256) of the given data on Boba Network.

> **Why Boba Network?** Build on the Hybrid Compute L2 enabling smart contracts to access AI models and Web2 APIs natively with HybridCompute 2.0 for native AI/API access, $70M ecosystem funding, OP Stack compatibility, and two-way offchain integration.

**Important**: Despite the method name, `web3_sha3` computes Keccak-256, which differs from the NIST-standardized SHA3-256. Ethereum adopted Keccak before NIST finalized the SHA-3 standard with different padding.

## When to Use This Method

`web3_sha3` is helpful for AI dApp developers, enterprise integration teams, and builders requiring offchain compute access:

- **Hash Verification** - Confirm that your local Keccak-256 implementation matches the node's output
- **Smart Contract Development** - Compute function selectors and event topic hashes needed for ABI encoding
- **Data Integrity Checks** - Hash data server-side via RPC and compare against expected values
- **Debugging** - Verify hashing results when troubleshooting transaction or contract interactions

## Best Practices

- Input data must be hex-encoded with a `0x` prefix; plain text strings will cause errors
- Use for quick Keccak-256 hashing without a client-side library, but prefer local hashing for performance
- Compute function selectors by taking the first 4 bytes of the hash result

## Request Parameters

- `data` (`DATA, required`): The hex-encoded data to hash (must be 0x prefixed)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "web3_sha3",
  "params": ["0x68656c6c6f"],
  "id": 1
}
```

## Response Fields

- `result` (`DATA, required`): The Keccak-256 hash of the provided data (32 bytes, 0x prefixed)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "invalid argument 0: hex string without 0x prefix"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "web3_sha3",
    "params": ["0x68656c6c6f"],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider, keccak256, toUtf8Bytes, hexlify } from 'ethers';

const provider = new JsonRpcProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY');

// Using RPC
const hash = await provider.send('web3_sha3', ['0x68656c6c6f']);
console.log('RPC hash:', hash);

// Using ethers locally (faster, no network call)
const localHash = keccak256(toUtf8Bytes('hello'));
console.log('Local hash:', localHash);

// Verify they match
console.log('Match:', hash === localHash);
```

```python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

# web3_sha3 - Boba Network RPC Method
data = '0x68656c6c6f'  # "hello" in hex
rpc_hash = w3.provider.make_request('web3_sha3', [data])['result']
print(f'RPC hash: {rpc_hash}')

# Using web3.py locally (faster)
local_hash = w3.keccak(text='hello').hex()
print(f'Local hash: 0x{local_hash}')

# Using requests directly
import requests

response = requests.post(
    'https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY',
    json={
        'jsonrpc': '2.0',
        'method': 'web3_sha3',
        'params': ['0x68656c6c6f'],
        'id': 1
    }
)
print(f'Hash: {response.json()["result"]}')
```

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/ethereum/go-ethereum/rpc"
    "golang.org/x/crypto/sha3"
)

func main() {
    client, err := rpc.Dial("https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    // Using RPC
    var rpcHash string
    err = client.CallContext(context.Background(), &rpcHash, "web3_sha3", "0x68656c6c6f")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("RPC hash: %s\n", rpcHash)

    // Using local Keccak-256
    hasher := sha3.NewLegacyKeccak256()
    hasher.Write([]byte("hello"))
    localHash := fmt.Sprintf("0x%x", hasher.Sum(nil))
    fmt.Printf("Local hash: %s\n", localHash)
}
```

## Common Use Cases

### 1. Function Selector Computation

Compute Solidity function selectors for ABI encoding:

```javascript
async function getFunctionSelector(provider, signature) {
  // Convert signature to hex
  const hexSignature = '0x' + Buffer.from(signature).toString('hex');

  // Hash via RPC
  const hash = await provider.send('web3_sha3', [hexSignature]);

  // Function selector is the first 4 bytes
  const selector = hash.slice(0, 10);
  console.log(`${signature} => ${selector}`);
  return selector;
}

// Example: compute selectors for common ERC-20 functions
const selectors = {
  'transfer(address,uint256)': await getFunctionSelector(provider, 'transfer(address,uint256)'),
  'balanceOf(address)': await getFunctionSelector(provider, 'balanceOf(address)'),
  'approve(address,uint256)': await getFunctionSelector(provider, 'approve(address,uint256)'),
};
```

### 2. Hash Verification Between Local and RPC

Verify your local hashing matches the node to catch library misconfigurations:

```javascript
async function verifyHashingConsistency(provider) {
  const testCases = [
    { input: '0x', label: 'empty bytes' },
    { input: '0x68656c6c6f', label: '"hello"' },
    { input: '0x0123456789abcdef', label: 'hex data' },
  ];

  for (const { input, label } of testCases) {
    const rpcHash = await provider.send('web3_sha3', [input]);
    const localHash = keccak256(input);

    const match = rpcHash === localHash;
    console.log(`${label}: ${match ? 'PASS' : 'FAIL'}`);

    if (!match) {
      console.error(`  RPC:   ${rpcHash}`);
      console.error(`  Local: ${localHash}`);
    }
  }
}
```

### 3. Event Topic Hash Generation

Generate event topic hashes for filtering logs:

```python
from web3 import Web3

def get_event_topic(w3, event_signature):
    """Generate the topic hash for an event signature."""
    hex_sig = '0x' + event_signature.encode().hex()
    result = w3.provider.make_request('web3_sha3', [hex_sig])
    return result['result']

w3 = Web3(Web3.HTTPProvider('https://api-boba-mainnet.n.dwellir.com/YOUR_API_KEY'))

# Common ERC-20 event topics
topics = {
    'Transfer(address,address,uint256)': get_event_topic(w3, 'Transfer(address,address,uint256)'),
    'Approval(address,address,uint256)': get_event_topic(w3, 'Approval(address,address,uint256)'),
}

for sig, topic in topics.items():
    print(f'{sig}\n  => {topic}')
```

## Related Methods

- [`eth_call`](https://www.dwellir.com/docs/boba-network/eth_call) - Execute a call without creating a transaction
- [`eth_getCode`](https://www.dwellir.com/docs/boba-network/eth_getCode) - Get contract bytecode at an address
- [`web3_clientVersion`](https://www.dwellir.com/docs/boba-network/web3_clientVersion) - Get node client version

---

## Bridge Hub - Polkadot Interoperability

## Why Build on Bridge Hub?

Bridge Hub is Polkadot's system parachain dedicated to secure cross-network connectivity. It hosts bridge light clients, routing logic, and XCM tooling that allow ecosystems such as Kusama and Ethereum to interoperate without trusted third parties.

### **Trustless Finality**

- **On-chain GRANDPA & BEEFY verification** – Embedded light clients validate both Polkadot and bridged consensus, enabling permissionless relayers.
- **Optimistic root tracking** – Finality proofs are persisted so downstream chains can verify historical headers.
- **Governed suspension modes** – Bridge operators can pause lanes per pallet when anomalies are detected.

### **Multi-Chain Connectors**

- **Polkadot  Kusama routing** – Bridge Westend/Bridge Bulletin pallets exchange relay-chain and parachain headers between ecosystems.
- **Snowbridge (Polkadot  Ethereum)** – Native integration of Snowbridge pallets for ERC-20 asset movement and message passing with Ethereum mainnet.
- **Message fan-out** – Bridge Bulletin instances distribute payloads to partner chains without duplicating relayer work.

### **Specialized Pallets**

- `pallet_bridge_grandpa` – Stores bridged finality proofs and validator sets.
- `pallet_bridge_parachains` – Syncs parachain headers required for XCM and HRMP messaging.
- `pallet_bridge_messages` – Manages outbound/inbound lanes, delivery proofs, and fee accounting for cross-chain messages.
- `pallet_xcm_bridge_hub` – Provides XCM helpers for routing assets and arbitrary instructions over bridge lanes.

## Quick Start with Bridge Hub

Connect to Bridge Hub using Dwellir's low-latency infrastructure:

Dwellir maintains Bridge Hub connectivity across Polkadot environments:

- **Polkadot Bridge Hub** – Production-grade routing parachain for trustless bridging.
- **Westend Bridge Hub** – Official Polkadot testnet used for validating upgrades before mainnet rollout.
- **Paseo Bridge Hub** – Community-driven public testnet mirroring upcoming Polkadot features.
- **Kusama Bridge Hub** – Economic canary network that ships features before Polkadot.

### Installation & Setup

Direct JSON-RPC
Polkadot.js
Substrate API (Rust)
Python (py-substrate-interface)

```bash
# Bridge Hub - Polkadot Interoperability
curl -X POST https://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'

# Fetch latest bridged Westend block hash
curl -X POST https://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "BridgeWestendGrandpa_finalityApi_bestFinalized",
      "0x"
    ],
    "id": 2
  }'
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

// Connect to Bridge Hub
const provider = new WsProvider('wss://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY');
const api = await ApiPromise.create({ provider });

// Confirm we are on Bridge Hub
const [chain, version] = await Promise.all([
  api.rpc.system.chain(),
  api.rpc.system.version()
]);
console.log(`Connected to ${chain} v${version}`);

// Inspect the latest bridged Westend finality proof
const bestWestendFinality = await api.query.bridgeWestendGrandpa.bestFinalized();
console.log('Best Westend header finalized on Bridge Hub:', bestWestendFinality.toString());

// Track Snowbridge outbound lane metrics
const outboundLanes = await api.query.bridgeWestendMessages.outboundLanes.entries();
outboundLanes.slice(0, 1).forEach(([key, value]) => {
  console.log('Outbound lane', key.args[0].toString(), value.toHuman());
});
```

```rust
use jsonrpsee::rpc_params;
use substrate_api_client::{rpc::JsonrpseeClient, Api, GetBlockHash};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = JsonrpseeClient::with_url("https://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY").await?;
    let api = Api::new(client).await?;

    // Fetch parachain head
    let head = api.get_finalized_head().await?;
    println!("Latest Bridge Hub head: {head}");

    // Query BEEFY justification root published by Bridge Hub
    let beefy_head: Option<String> = api
        .rpc()
        .request("beefy_getFinalizedHead", rpc_params![])
        .await?;
    println!("Latest BEEFY finalized head: {:?}", beefy_head);

    // Inspect permissionless lane pallet state
    let storage_key = api.metadata().storage_map_key(
        "BridgeRelayersForPermissionlessLanes",
        "RelayerConfig",
        &0u32,
    )?;
    let config: Option<Vec<u8>> = api.get_storage_by_key(storage_key, None).await?;
    println!("Permissionless relayer config: {:?}", config);

    Ok(())
}
```

```python
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(
    url="wss://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY"
)

chain_name = substrate.rpc_request("system_chain", [])
print("Chain:", chain_name["result"])

# Retrieve head committed to the Kusama bridge lane
best_header = substrate.query(
    module='BridgeWestendGrandpa',
    storage_function='BestFinalized'
)
print("Best bridged Westend header:", best_header)

# Check outbound message lane status for Snowbridge
lane_status = substrate.query(
    module='EthereumOutboundQueue',
    storage_function='Outbox',
    params=[0]
)
print("Snowbridge outbound lane 0 status:", lane_status.value)
```

## Network Information

| Parameter    | Value         | Details                   |
| ------------ | ------------- | ------------------------- |
| Genesis Hash | 0xdcf691b5... | Bridge Hub (Polkadot)     |
| Block Time   | 6 seconds     | Aura + Relay finality     |
| Native Token | DOT           | Fees paid via Relay Chain |
| Parachain ID | 1002          | Polkadot system parachain |

### Network Details

| Parameter            | Value                                                                | Details |
| -------------------- | -------------------------------------------------------------------- | ------- |
| Relay Chain          | Polkadot                                                             |         |
| Genesis Hash         | `0xdcf691b5a3fbe24adc99ddc959c0561b973e329b1aef4c4b22e7bb2ddecb4464` |         |
| Consensus            | Collator-produced blocks with Polkadot relay GRANDPA finality        |         |
| Bridged Counterparts | Kusama (Westend), Bridge Bulletin, Ethereum (Snowbridge)             |         |
| Security             | On-chain slashable relayer incentives via `pallet_bridge_relayers`   |         |

## API Reference

Bridge Hub exposes the standard Substrate namespaces alongside bridge-specific RPCs for GRANDPA, BEEFY, and Snowbridge messaging. Public `bridge_*` proof namespaces are not yet exposed on current nodes.

## Bridge Protocols

### Snowbridge (Ethereum  Polkadot)

- **BEEFY light client** verifies Ethereum finality proofs and publishes outbound commitments.
- **Inbound queue** accepts ERC-20 and arbitrary message payloads via event proofs.
- **Outbound queue** batches Polkadot-origin messages and anchors them on Ethereum.

### Kusama Bridge (Westend  Polkadot)

- **BridgeWestendGrandpa** pallet syncs Westend finality headers and validator sets.
- **BridgeWestendParachains** tracks parachain heads to validate XCM and HRMP messages.
- **BridgeWestendMessages** exposes lane metrics, delivery confirmations, and fee accounting.

### Bridge Bulletin

- **Polkadot Bulletin** acts as a hub-and-spoke relay for partners requiring broadcast style delivery.
- **Permissionless lanes** allow new chains to request connectivity without runtime upgrades.

## XCM Integration

Bridge Hub exposes `pallet_xcm_bridge_hub` helpers for routing XCM messages over bridge lanes.

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

const api = await ApiPromise.create({
  provider: new WsProvider('wss://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY')
});

const laneId = 0; // Snowbridge outbound lane
const beneficiary = {
  parents: 2,
  interior: { X1: { AccountId32: { network: 'Any', id: '0x...' } } }
};
const assets = {
  parents: 1,
  interior: 'Here'
};

const tx = api.tx.xcmBridgeHub.sendAssetsOverBridge(
  laneId,
  beneficiary,
  assets,
  new BN('100000000000'),
  'Unlimited'
);

await tx.signAndSend('//Alice', ({ status }) => {
  if (status.isInBlock) {
    console.log('Bridge XCM submitted in block', status.asInBlock.toString());
  }
});
```

## Development Guides

### 1. Finality Relayer Checklist

- Subscribe to `bridgeWestendGrandpa.bestFinalized` and `bridgePolkadotBulletinGrandpa.bestFinalized` storage changes.
- Submit new proofs via `BridgeGrandpa.submit_finality_proof` extrinsics when headers advance by configured intervals.
- Monitor `pallet_bridge_relayers::Rewards` to reconcile payments.

### 2. Message Relayer Operations

- Watch `bridgeWestendMessages.outboundLanes(laneId)` for `latest_generated_nonce` vs `latest_received_nonce`.
- When public proof RPCs are released, fetch Merkle lane proofs before submitting to bridged chains (currently relayers must build proofs off-chain).
- Handle rejections by inspecting `bridgeHub` events: `MessageDispatched` and `MessageRejected`.

### 3. Observability & Alerting

- Track BEEFY justifications with `beefy_subscribeJustifications` for Snowbridge health.
- Use `state_traceBlock` on Bridge Hub to replay block execution when diagnosing bridge stalls.
- Surface metrics such as `MessageQueue.Processed` events to confirm queue throughput.

## Monitoring & Troubleshooting

| Scenario                       | Diagnostic Steps                                                                | Resolution                                                         |
| ------------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| Lane backpressure              | Compare `latest_generated_nonce` and `latest_confirmed_nonce` on outbound lanes | Increase relayer frequency or investigate failed proofs            |
| Proof rejected on target chain | Rebuild proofs off-chain and verify against the target runtime                  | Re-submit with corrected payload or wait for next finalized header |
| BEEFY stalled                  | Check `beefy_subscribeJustifications` stream and collator logs                  | Restart Snowbridge relayer or investigate validator availability   |

With Dwellir endpoints you get globally replicated infrastructure, WebSocket support for proof streaming, and archive access for rebuilding historical proofs.

---

## author_pendingExtrinsics - Bridge Hub RPC Method

Returns all pending extrinsics currently in the transaction pool on Bridge Hub. These are signed extrinsics that have been submitted but not yet included in a finalized block.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`author_pendingExtrinsics` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Transaction Confirmation** -- Verify whether a submitted extrinsic is still pending or has been included in a block on Bridge Hub
- **Mempool Monitoring** -- Monitor the transaction pool size and activity for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Network Congestion Analysis** -- Gauge current network load by inspecting the number and type of pending extrinsics
- **Validator Tooling** -- Build block authoring tools that inspect the ready queue before producing blocks

## Best Practices

- Response can be large on congested networks -- filter by sender address client-side
- Not available on all node configurations (some providers disable author namespace)
- Use for mempool inspection and transaction congestion diagnosis
- Pending extrinsics are not guaranteed to be included -- monitor with confirmation polling

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_pendingExtrinsics",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded SCALE-encoded signed extrinsics currently in the transaction pool

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x2d0284ff...",
    "0x3102840f..."
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_pendingExtrinsics",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const pending = await api.rpc.author.pendingExtrinsics();
console.log('Pending extrinsics:', pending.length);

pending.forEach((ext, idx) => {
  console.log(`${idx}: ${ext.method.section}.${ext.method.method}`);
  console.log(`   Signer: ${ext.signer.toString()}`);
  console.log(`   Nonce: ${ext.nonce.toString()}`);
  console.log(`   Tip: ${ext.tip.toString()}`);
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_pendingExtrinsics',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`${result.length} pending extrinsics in pool`);
```

```python
import requests

def get_pending_extrinsics():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'author_pendingExtrinsics',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

pending = get_pending_extrinsics()
print(f'Pending extrinsics: {len(pending)}')

# author_pendingExtrinsics - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('author_pendingExtrinsics', [])['result']
print(f'Pending extrinsics: {len(result)}')

for i, ext_hex in enumerate(result):
    print(f'  {i}: {ext_hex[:40]}...')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_pendingExtrinsics",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let pending = result["result"].as_array().unwrap();

    println!("Pending extrinsics: {}", pending.len());
    for (i, ext) in pending.iter().enumerate() {
        let hex = ext.as_str().unwrap();
        println!("  {}: {}...", i, &hex[..std::cmp::min(40, hex.len())]);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Transaction Pool Monitor

Continuously monitor the Bridge Hub transaction pool and alert on unusual activity:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorPool(api, interval = 6000) {
  let previousCount = 0;

  setInterval(async () => {
    const pending = await api.rpc.author.pendingExtrinsics();
    const count = pending.length;

    if (count !== previousCount) {
      console.log(`Pool size changed: ${previousCount} -> ${count}`);

      if (count > 100) {
        console.warn('High pool activity detected!');
      }
    }

    // Analyze pending extrinsic types
    const byPallet = {};
    pending.forEach((ext) => {
      const key = `${ext.method.section}.${ext.method.method}`;
      byPallet[key] = (byPallet[key] || 0) + 1;
    });

    if (Object.keys(byPallet).length > 0) {
      console.log('Pending by type:', byPallet);
    }

    previousCount = count;
  }, interval);
}
```

### 2. Verify Transaction Submission

Check that a submitted extrinsic appears in the pool:

```javascript
async function verifyInPool(api, txHash) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const found = pending.find((ext) => ext.hash.toHex() === txHash);

  if (found) {
    console.log(`Transaction ${txHash} is in the pool`);
    console.log(`  Call: ${found.method.section}.${found.method.method}`);
    return true;
  }

  console.log(`Transaction ${txHash} not found in pool (may already be included)`);
  return false;
}
```

### 3. Pool Congestion Analysis

Analyze network congestion to decide on tip amounts:

```javascript
async function analyzeCongestion(api) {
  const pending = await api.rpc.author.pendingExtrinsics();

  const tips = pending.map((ext) => ext.tip.toBigInt());
  const totalTips = tips.reduce((sum, tip) => sum + tip, 0n);
  const avgTip = tips.length > 0 ? totalTips / BigInt(tips.length) : 0n;
  const maxTip = tips.length > 0 ? tips.reduce((a, b) => (a > b ? a : b), 0n) : 0n;

  return {
    poolSize: pending.length,
    averageTip: avgTip.toString(),
    maxTip: maxTip.toString(),
    congested: pending.length > 50
  };
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitExtrinsic) -- Submit a signed extrinsic to the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bridge-hub/payment_queryInfo) -- Estimate fees for an extrinsic before submission
- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) -- Get a finalized block to see which extrinsics were included

---

## author_rotateKeys - Bridge Hub RPC Method

Generate a new set of session keys on Bridge Hub. This method creates fresh cryptographic keys for all session key types (e.g., BABE, GRANDPA, ImOnline, ParaValidator, AuthorityDiscovery) and stores them in the node's local keystore. The returned concatenated public keys must be registered on-chain via `session.setKeys`.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`author_rotateKeys` is critical for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Validator Setup** - Generate initial session keys when setting up a new validator on trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Key Rotation** - Periodically rotate keys for operational security best practices
- **Recovery** - Generate replacement keys after a potential key compromise or node migration
- **Validator Upgrades** - Produce new keys when moving a validator to new hardware

## Best Practices

- Session key rotation requires validator node access -- not available to most API consumers
- Requires node-level authorization and is typically automated by validator infrastructure
- New session keys take effect at the next session boundary
- Most API users should not need this method

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_rotateKeys",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): Hex-encoded concatenation of all session key public keys (SCALE-encoded)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "RPC call is unsafe to be called externally"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_rotateKeys - Bridge Hub RPC Method
curl -X POST http://127.0.0.1:9944 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_rotateKeys",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

// Connect to your LOCAL validator node
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });

// Generate new session keys
const keys = await api.rpc.author.rotateKeys();
console.log('New session keys:', keys.toHex());

// Register the keys on-chain
const keyring = new Keyring({ type: 'sr25519' });
const validatorAccount = keyring.addFromUri('//ValidatorStash');

const tx = api.tx.session.setKeys(keys, '0x');
const hash = await tx.signAndSend(validatorAccount);
console.log('setKeys transaction hash:', hash.toHex());

await api.disconnect();
```

```python
import requests

def rotate_keys():
    # Always call on your LOCAL validator node
    url = 'http://127.0.0.1:9944'

    payload = {
        'jsonrpc': '2.0',
        'method': 'author_rotateKeys',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"Error: {result['error']['message']}")

    return result['result']

try:
    session_keys = rotate_keys()
    print(f'New session keys: {session_keys}')
    print('Next step: Submit session.setKeys extrinsic with these keys')
except Exception as e:
    print(f'Failed: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to LOCAL validator node
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "ws://127.0.0.1:9944"
    ).await?;

    let keys: Value = api.rpc()
        .request("author_rotateKeys", subxt::rpc_params![])
        .await?;

    println!("New session keys: {}", keys);
    println!("Submit session.setKeys with these keys");

    Ok(())
}
```

## Common Use Cases

### 1. Complete Validator Setup Workflow

Full end-to-end validator setup on Bridge Hub:

```javascript
async function setupValidator(api, stashAccount) {
  // Step 1: Generate session keys
  const keys = await api.rpc.author.rotateKeys();
  console.log('Generated session keys:', keys.toHex());

  // Step 2: Register keys on-chain
  const setKeysTx = api.tx.session.setKeys(keys, '0x');
  await new Promise((resolve, reject) => {
    setKeysTx.signAndSend(stashAccount, ({ status, events }) => {
      if (status.isFinalized) {
        const success = events.some(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        if (success) {
          console.log('Session keys registered successfully');
          resolve();
        } else {
          reject(new Error('setKeys transaction failed'));
        }
      }
    });
  });

  // Step 3: Verify registration
  const nextKeys = await api.query.session.nextKeys(stashAccount.address);
  console.log('Keys registered for next session:', nextKeys.isSome);
}
```

### 2. Scheduled Key Rotation

Automate periodic key rotation for security:

```javascript
async function scheduleKeyRotation(api, validatorAccount, intervalDays = 30) {
  const intervalMs = intervalDays * 24 * 60 * 60 * 1000;

  async function rotateAndRegister() {
    try {
      const newKeys = await api.rpc.author.rotateKeys();
      console.log(`Rotated keys at ${new Date().toISOString()}`);

      const tx = api.tx.session.setKeys(newKeys, '0x');
      await tx.signAndSend(validatorAccount);
      console.log('New keys registered - active next session');
    } catch (error) {
      console.error('Key rotation failed:', error.message);
    }
  }

  // Initial rotation
  await rotateAndRegister();

  // Schedule future rotations
  setInterval(rotateAndRegister, intervalMs);
}
```

## Validator Setup Workflow

1. **Generate keys** - Call `author_rotateKeys` on your validator node
2. **Register on-chain** - Submit `session.setKeys(keys, proof)` extrinsic from your stash account
3. **Wait for session** - Keys become active at the start of the next session
4. **Verify** - Query `session.nextKeys` to confirm registration

## Security Considerations

- **Local access only** - Only call this method on your own validator node via localhost
- **Never expose publicly** - This RPC method is marked as `unsafe` and should not be accessible from the internet
- **Keystore security** - Session keys are stored in the node's keystore directory on disk
- **Rotate regularly** - Follow a key rotation schedule to limit exposure from potential compromises
- **Backup awareness** - New keys replace old ones in the keystore; old keys cannot be recovered

## Related Methods

- `author_hasSessionKeys` - Check if session keys exist in the keystore
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitExtrinsic) - Submit the `setKeys` transaction
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bridge-hub/author_pendingExtrinsics) - View pending transactions
- `session_nextKeys` - Query registered session keys on-chain

---

## author_submitAndWatchExtrinsic - Bridge Hub RPC Method

Submits a signed extrinsic to Bridge Hub and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Bridge Hub
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic - Bridge Hub RPC Method
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://bridge-hub-polkadot-rpc.n.dwellir.com

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://bridge-hub-polkadot-rpc.n.dwellir.com');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://bridge-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://bridge-hub-polkadot-rpc.n.dwellir.com").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bridge-hub/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/bridge-hub/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) -- Get the hash of the latest finalized block

---

## author_submitExtrinsic - Bridge Hub RPC Method

Submits a fully signed extrinsic to Bridge Hub for inclusion in a future block. The extrinsic enters the transaction pool and is propagated to other nodes. This is the primary method for broadcasting any on-chain operation, including balance transfers, staking, governance, and pallet interactions.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`author_submitExtrinsic` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Token Transfers** -- Send native tokens or assets between accounts on Bridge Hub
- **Staking and Governance** -- Submit staking nominations, validator operations, and governance votes for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Smart Contract Interaction** -- Call ink! or EVM smart contracts deployed on the chain
- **Automated Systems** -- Build bots, keepers, and automated transaction pipelines that submit extrinsics programmatically

## Best Practices

- Sign extrinsics client-side before submission -- never expose private keys to the node
- Returns the transaction hash immediately after submission -- polling is required for confirmation
- Monitor inclusion via `chain_getBlock` or subscribe to `chain_subscribeNewHeads`
- Equivalent to `eth_sendRawTransaction` on EVM chains

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-encoded signed extrinsic including signature, nonce, era, and tip

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitExtrinsic",
  "params": ["0x4d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The extrinsic hash (Blake2-256) as a hex string, used to track the transaction

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
}
```

## Error Responses

### Error Response (invalid transaction)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction has a bad signature"
  }
}
```

### Error Response (nonce too low)

- Code: `1010`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1010,
    "message": "Invalid Transaction",
    "data": "Transaction is outdated"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Set up sender keypair
const keyring = new Keyring({ type: 'sr25519' });
const sender = keyring.addFromUri('//Alice'); // Use your actual key in production

// Build and send a transfer
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Adjust for chain decimals

const hash = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender);

console.log('Transaction hash:', hash.toHex());

// With status tracking
const unsub = await api.tx.balances
  .transferKeepAlive(recipient, amount)
  .signAndSend(sender, ({ status, events, dispatchError }) => {
    if (status.isInBlock) {
      console.log(`Included in block: ${status.asInBlock.toHex()}`);
    }
    if (status.isFinalized) {
      console.log(`Finalized in block: ${status.asFinalized.toHex()}`);

      if (dispatchError) {
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(
            dispatchError.asModule
          );
          console.error(`Error: ${section}.${name}: ${docs.join(' ')}`);
        } else {
          console.error('Error:', dispatchError.toString());
        }
      } else {
        console.log('Transaction succeeded');
      }

      unsub();
    }
  });

// Low-level: submit a pre-signed extrinsic
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitExtrinsic',
    params: ['0x4d0284ff...'], // pre-signed extrinsic hex
    id: 1
  })
});

const { result, error } = await response.json();
if (error) {
  console.error('Submission failed:', error.message, error.data);
} else {
  console.log('Extrinsic hash:', result);
}
```

```python
import requests

def submit_extrinsic(extrinsic_hex):
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'author_submitExtrinsic',
            'params': [extrinsic_hex],
            'id': 1
        }
    )
    result = response.json()
    if 'error' in result:
        raise Exception(f"Submission failed: {result['error']}")
    return result['result']

# author_submitExtrinsic - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')

# Create keypair
keypair = Keypair.create_from_uri('//Alice')  # Use your actual key

# Compose a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create, sign, and submit extrinsic
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)

print(f'Extrinsic hash: {receipt.extrinsic_hash}')
print(f'Block hash: {receipt.block_hash}')
print(f'Success: {receipt.is_success}')

if not receipt.is_success:
    print(f'Error: {receipt.error_message}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Submit a pre-signed extrinsic
    let extrinsic_hex = "0x4d0284ff..."; // Build with subxt or offline signer

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "author_submitExtrinsic",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    if let Some(error) = result.get("error") {
        eprintln!("Submission failed: {} - {}",
            error["message"],
            error.get("data").unwrap_or(&json!(""))
        );
    } else {
        println!("Extrinsic hash: {}", result["result"]);
    }

    Ok(())
}

// For full signing and submission in Rust, use the `subxt` crate:
// https://github.com/paritytech/subxt
//
// use subxt::{OnlineClient, PolkadotConfig};
// use subxt_signer::sr25519::dev;
//
// let api = OnlineClient::<PolkadotConfig>::from_url("https://bridge-hub-polkadot-rpc.n.dwellir.com").await?;
// let dest = dev::bob().public_key().into();
// let tx = polkadot::tx().balances().transfer_keep_alive(dest, 1_000_000_000_000);
// let hash = api.tx().sign_and_submit_default(&tx, &dev::alice()).await?;
```

## Common Use Cases

### 1. Transfer with Fee Pre-Check

Verify fees and balance before submitting a transfer:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function safeTransfer(api, sender, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

  // Step 1: Estimate fee
  const info = await transfer.paymentInfo(sender.address);
  const fee = info.partialFee.toBigInt();
  console.log(`Estimated fee: ${info.partialFee.toHuman()}`);

  // Step 2: Check balance
  const account = await api.query.system.account(sender.address);
  const free = account.data.free.toBigInt();
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const totalCost = BigInt(amount) + fee;

  if (free - totalCost < existentialDeposit) {
    throw new Error(`Insufficient balance. Need ${totalCost}, have ${free}`);
  }

  // Step 3: Submit
  const hash = await transfer.signAndSend(sender);
  console.log(`Submitted: ${hash.toHex()}`);
  return hash;
}
```

### 2. Batch Transaction Submission

Submit multiple operations in a single extrinsic:

```javascript
async function submitBatch(api, sender, calls) {
  const batch = api.tx.utility.batchAll(calls);

  // Estimate total fee
  const info = await batch.paymentInfo(sender.address);
  console.log(`Batch fee: ${info.partialFee.toHuman()} for ${calls.length} calls`);

  // Submit with event tracking
  return new Promise((resolve, reject) => {
    batch.signAndSend(sender, ({ status, events, dispatchError }) => {
      if (dispatchError) {
        if (dispatchError.isModule) {
          const decoded = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        const successEvents = events.filter(({ event }) =>
          api.events.system.ExtrinsicSuccess.is(event)
        );
        resolve({
          blockHash: status.asFinalized.toHex(),
          success: successEvents.length > 0,
          events: events.length
        });
      }
    });
  });
}

// Usage: batch multiple transfers
const calls = [
  api.tx.balances.transferKeepAlive(recipient1, amount1),
  api.tx.balances.transferKeepAlive(recipient2, amount2),
  api.tx.balances.transferKeepAlive(recipient3, amount3)
];

const result = await submitBatch(api, sender, calls);
```

### 3. Nonce Management for Sequential Transactions

Submit multiple transactions in rapid succession with correct nonce handling:

```javascript
async function submitSequential(api, sender, extrinsics) {
  // Get the starting nonce
  let nonce = await api.rpc.system.accountNextIndex(sender.address);

  const hashes = [];
  for (const ext of extrinsics) {
    const hash = await ext.signAndSend(sender, { nonce });
    hashes.push(hash.toHex());
    console.log(`Submitted with nonce ${nonce}: ${hash.toHex()}`);
    nonce = nonce.addn(1);
  }

  return hashes;
}
```

## Related Methods

- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bridge-hub/author_pendingExtrinsics) -- Check the transaction pool for pending extrinsics
- [`payment_queryInfo`](https://www.dwellir.com/docs/bridge-hub/payment_queryInfo) -- Estimate fees before submitting
- `system_accountNextIndex` -- Get the next valid nonce for an account
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call runtime APIs (e.g., for nonce via `AccountNonceApi`)
- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) -- Verify extrinsic inclusion in a block

---

## beefy_getFinalizedHead - Bridge Hub RPC Method

# beefy_getFinalizedHead - Bridge Hub RPC Method

Returns the block hash of the latest BEEFY-finalized block on Bridge Hub. BEEFY (Bridge Efficiency Enabling Finality Yielder) provides additional finality proofs that are optimized for light clients and cross-chain bridges, using compact aggregated signatures instead of full GRANDPA justifications.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`beefy_getFinalizedHead` is important for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Cross-Chain Bridges** - Verify finality proofs efficiently for bridge operations on trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Light Clients** - Verify finality without downloading full GRANDPA justifications
- **Trustless Bridges** - Generate compact finality proofs that can be verified on external chains
- **Bridge Monitoring** - Track BEEFY finality progress relative to GRANDPA finality

## Best Practices

- BEEFY (Bridge Efficiency Enabling Finality Yielder) protocol secures cross-chain bridge finality
- Returns the hash of the latest BEEFY-finalized block for proof generation
- Use for cross-chain verification rather than regular block finality (use `chain_getFinalizedHead` for that)
- Required for bridge relayers that verify finality across connected chains

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "beefy_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest BEEFY-finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response (BEEFY Not Enabled)

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "BEEFY is not enabled on this chain"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "beefy_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

try {
  // Get BEEFY finalized head
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  console.log('BEEFY finalized:', beefyHead.toHex());

  // Compare with GRANDPA finalized
  const grandpaHead = await api.rpc.chain.getFinalizedHead();
  console.log('GRANDPA finalized:', grandpaHead.toHex());

  // Get block numbers for comparison
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const grandpaBlock = await api.rpc.chain.getBlock(grandpaHead);

  const beefyNum = beefyBlock.block.header.number.toNumber();
  const grandpaNum = grandpaBlock.block.header.number.toNumber();
  console.log(`BEEFY lag behind GRANDPA: ${grandpaNum - beefyNum} blocks`);
} catch (error) {
  console.error('BEEFY may not be enabled:', error.message);
}

await api.disconnect();
```

```python
import requests

def get_beefy_finalized_head():
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'beefy_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"BEEFY error: {result['error']['message']}")

    return result['result']

def get_grandpa_finalized_head():
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 2
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

try:
    beefy_hash = get_beefy_finalized_head()
    grandpa_hash = get_grandpa_finalized_head()
    print(f'BEEFY finalized: {beefy_hash}')
    print(f'GRANDPA finalized: {grandpa_hash}')
except Exception as e:
    print(f'Error: {e}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Call beefy_getFinalizedHead via raw RPC
    let beefy_head: Value = api.rpc()
        .request("beefy_getFinalizedHead", subxt::rpc_params![])
        .await?;

    println!("BEEFY finalized: {}", beefy_head);

    // Compare with GRANDPA finalized
    let grandpa_head = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("GRANDPA finalized: {:?}", grandpa_head);

    Ok(())
}
```

## Common Use Cases

### 1. Bridge Finality Verification

Verify BEEFY finality before relaying messages on a cross-chain bridge:

```javascript
async function verifyBridgeFinality(api, targetBlockHash) {
  const beefyHead = await api.rpc.beefy.getFinalizedHead();
  const beefyBlock = await api.rpc.chain.getBlock(beefyHead);
  const beefyNumber = beefyBlock.block.header.number.toNumber();

  const targetBlock = await api.rpc.chain.getBlock(targetBlockHash);
  const targetNumber = targetBlock.block.header.number.toNumber();

  if (beefyNumber >= targetNumber) {
    console.log(`Block #${targetNumber} has BEEFY finality - safe to relay`);
    return true;
  } else {
    console.log(`Waiting: BEEFY at #${beefyNumber}, target at #${targetNumber}`);
    return false;
  }
}
```

### 2. BEEFY vs GRANDPA Finality Monitor

Track the gap between the two finality gadgets:

```javascript
async function monitorFinalityGadgets(api) {
  setInterval(async () => {
    try {
      const [beefyHead, grandpaHead] = await Promise.all([
        api.rpc.beefy.getFinalizedHead(),
        api.rpc.chain.getFinalizedHead()
      ]);

      const [beefyBlock, grandpaBlock] = await Promise.all([
        api.rpc.chain.getBlock(beefyHead),
        api.rpc.chain.getBlock(grandpaHead)
      ]);

      const beefyNum = beefyBlock.block.header.number.toNumber();
      const grandpaNum = grandpaBlock.block.header.number.toNumber();
      const lag = grandpaNum - beefyNum;

      console.log(`GRANDPA: #${grandpaNum} | BEEFY: #${beefyNum} | Lag: ${lag} blocks`);
    } catch (error) {
      console.error('Monitor error:', error.message);
    }
  }, 12000);
}
```

## BEEFY vs GRANDPA Finality

| Aspect                | GRANDPA                                | BEEFY                                      |
| --------------------- | -------------------------------------- | ------------------------------------------ |
| **Purpose**           | Primary chain finality                 | Bridge-optimized finality                  |
| **Proof Size**        | Larger (full validator set signatures) | Compact (aggregated BLS signatures)        |
| **Latency**           | Immediate after supermajority          | Slightly delayed behind GRANDPA            |
| **Verification Cost** | Higher on external chains              | Lower - designed for on-chain verification |
| **Use Case**          | On-chain consensus finality            | Cross-chain bridges and light clients      |

## Availability

BEEFY is enabled on Polkadot and Kusama relay chains and some parachains. If BEEFY is not active on the chain you are querying, this method will return an error. Check chain documentation or try calling the method to confirm availability.

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) - Get GRANDPA finalized head
- [`grandpa_roundState`](https://www.dwellir.com/docs/bridge-hub/grandpa_roundState) - Monitor GRANDPA consensus state
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) - Subscribe to GRANDPA finalized blocks

---

## chain_getBlock - Bridge Hub RPC Method

Retrieves complete block information from Bridge Hub, including the block header, extrinsics, and justifications.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## Use Cases

The `chain_getBlock` method is essential for:

- **Block explorers** - Display complete block information
- **Chain analysis** - Analyze block production patterns
- **Transaction verification** - Confirm extrinsic inclusion for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Data indexing** - Build historical blockchain databases

## Best Practices

- Cache block data by hash -- blocks are immutable once finalized on Substrate chains
- Use `chain_getBlockHash` first to resolve block number to hash before calling this method
- Handle `null` results gracefully for non-existent blocks
- Combine with `chain_getFinalizedHead` for consensus-safe block retrieval

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlock",
  "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
  "id": 1
}
```

## Response Fields

- `block` (`Object, required`): Complete block data
- `block.header` (`Object, required`): Block header information
- `block.header.parentHash` (`String, required`): Hash of the parent block
- `block.header.number` (`String, required`): Block number (hex-encoded)
- `block.header.stateRoot` (`String, required`): Root of the state trie
- `block.header.extrinsicsRoot` (`String, required`): Root of the extrinsics trie
- `block.extrinsics` (`Array, required`): Array of extrinsics in the block
- `justifications` (`Array, required`): Block justifications (if available)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "block": {},
    "block.header": {},
    "block.header.parentHash": "<value>",
    "block.header.number": "<value>",
    "block.header.stateRoot": "<value>",
    "block.header.extrinsicsRoot": "<value>",
    "block.extrinsics": [],
    "justifications": []
  }
}
```

## Code Examples

cURL
JavaScript
Python

```bash
# chain_getBlock - Bridge Hub RPC Method
curl https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": [],
    "id": 1
  }'

# Get specific block
curl https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlock",
    "params": ["0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get latest block
const latestHash = await api.rpc.chain.getBlockHash();
const latestBlock = await api.rpc.chain.getBlock(latestHash);

console.log('Latest block:', {
  number: latestBlock.block.header.number.toNumber(),
  hash: latestHash.toHex(),
  extrinsicsCount: latestBlock.block.extrinsics.length
});

// Get specific block
const blockHash = '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3';
const block = await api.rpc.chain.getBlock(blockHash);
console.log('Block extrinsics:', block.block.extrinsics.length);

await api.disconnect();
```

```python
import requests
import json

def get_block(block_hash=None):
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlock',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    data = response.json()

    if 'error' in data:
        raise Exception(f"RPC Error: {data['error']}")

    return data['result']

# Get latest block
latest_block = get_block()
block_number = int(latest_block['block']['header']['number'], 16)
print(f'Latest block number: {block_number}')

# Get specific block
specific_block = get_block('0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3')
print(f"Extrinsics count: {len(specific_block['block']['extrinsics'])}")
```

## Related Methods

- [`chain_getBlockHash`](https://www.dwellir.com/docs/bridge-hub/chain_getBlockHash) - Get block hash by number
- [`chain_getHeader`](https://www.dwellir.com/docs/bridge-hub/chain_getHeader) - Get block header only
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) - Get finalized block hash

---

## chain_getBlockHash - Bridge Hub RPC Method

Returns the block hash for a given block number on Bridge Hub. This is the primary method for converting block numbers into block hashes, which are required by most other chain RPC methods.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`chain_getBlockHash` is fundamental for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Historical Queries** - Convert block numbers to hashes for state queries at specific heights on Bridge Hub
- **Block Navigation** - Navigate the blockchain history for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Data Indexing** - Build block number-to-hash mappings for indexers and explorers
- **Cross-Reference** - Translate block numbers from events or logs into hashes for detailed lookups

## Best Practices

- Use before `chain_getBlock` if you need hash-based block lookup on Bridge Hub
- Block numbers may change during chain reorganizations -- hashes are immutable
- Returns `null` for future blocks that do not exist yet
- Cache the genesis block hash as a known reference point

## Request Parameters

- `blockNumber` (`Number, optional`): Block number to look up. If omitted, returns the hash of the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getBlockHash",
  "params": [1000000],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte block hash, or null if block number does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block number"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getBlockHash - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [1000000],
    "id": 1
  }'

# Get hash for the latest block
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getBlockHash",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get hash for specific block number
const blockNumber = 1000000;
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
console.log(`Block ${blockNumber} hash:`, blockHash.toHex());

// Get hash for latest block
const latestHash = await api.rpc.chain.getBlockHash();
console.log('Latest block hash:', latestHash.toHex());

// Get genesis block hash
const genesisHash = await api.rpc.chain.getBlockHash(0);
console.log('Genesis hash:', genesisHash.toHex());

await api.disconnect();
```

```python
import requests

def get_block_hash(block_number=None):
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'
    params = [block_number] if block_number is not None else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getBlockHash',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get specific block hash
block_hash = get_block_hash(1000000)
print(f'Block 1000000 hash: {block_hash}')

# Get latest block hash
latest_hash = get_block_hash()
print(f'Latest block hash: {latest_hash}')

# Get genesis hash
genesis_hash = get_block_hash(0)
print(f'Genesis hash: {genesis_hash}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Get hash for a specific block number
    let block_hash = api.rpc()
        .chain_get_block_hash(Some(1_000_000u32.into()))
        .await?;

    println!("Block 1000000 hash: {:?}", block_hash);

    // Get latest block hash
    let latest_hash = api.rpc()
        .chain_get_block_hash(None)
        .await?;

    println!("Latest block hash: {:?}", latest_hash);

    Ok(())
}
```

## Common Use Cases

### 1. Block Range Iterator

Iterate over a range of blocks on Bridge Hub for indexing:

```javascript
async function iterateBlocks(api, startBlock, endBlock) {
  for (let num = startBlock; num <= endBlock; num++) {
    const hash = await api.rpc.chain.getBlockHash(num);
    const block = await api.rpc.chain.getBlock(hash);

    console.log(`Block #${num}: ${block.block.extrinsics.length} extrinsics`);
  }
}
```

### 2. Historical State Query

Query Bridge Hub state at a specific block height:

```javascript
async function getBalanceAtBlock(api, address, blockNumber) {
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
  const apiAt = await api.at(blockHash);
  const account = await apiAt.query.system.account(address);

  return {
    blockNumber,
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString()
  };
}
```

### 3. Genesis Hash Verification

Verify you are connected to the correct Bridge Hub network:

```javascript
async function verifyNetwork(api, expectedGenesisHash) {
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  if (genesisHash.toHex() !== expectedGenesisHash) {
    throw new Error(`Wrong network! Expected ${expectedGenesisHash}, got ${genesisHash.toHex()}`);
  }

  console.log('Connected to correct network');
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) - Get full block data by hash
- [`chain_getHeader`](https://www.dwellir.com/docs/bridge-hub/chain_getHeader) - Get block header by hash
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) - Get the latest finalized block hash

---

## chain_getFinalizedHead - Bridge Hub RPC Method

Returns the hash of the last finalized block on Bridge Hub. Finalized blocks have been confirmed by the GRANDPA finality gadget and are guaranteed to never be reverted.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`chain_getFinalizedHead` is critical for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Exchange Deposits** - Only credit user funds after the block has been finalized on Bridge Hub
- **Transaction Confirmation** - Verify transactions have achieved irreversible finality for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Safe Checkpoints** - Use finalized blocks as safe anchors for indexing and state queries
- **Bridge Operations** - Confirm source-chain finality before executing cross-chain transfers

## Best Practices

- Finalized blocks are irreversible and safe for all consensus-critical operations
- Use lower polling frequency than new heads -- finalization is slower
- Combine with `chain_getBlock` for full block data on finalized blocks
- For bridge applications, use `beefy_getFinalizedHead` for cross-chain proofs

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getFinalizedHead",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Hash, required`): Hex-encoded 32-byte hash of the latest finalized block

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x5d83f9a0c22ef15a5e4e8b7f7b3b0c3a1d6e9f2a4b7c8d0e1f2a3b4c5d6e7f80"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getFinalizedHead",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get finalized block hash
const finalizedHash = await api.rpc.chain.getFinalizedHead();
console.log('Finalized block hash:', finalizedHash.toHex());

// Get finalized block details
const block = await api.rpc.chain.getBlock(finalizedHash);
const blockNumber = block.block.header.number.toNumber();
console.log('Finalized block number:', blockNumber);

// Compare with best block to see finality lag
const bestHeader = await api.rpc.chain.getHeader();
const lag = bestHeader.number.toNumber() - blockNumber;
console.log(`Finality lag: ${lag} blocks`);

await api.disconnect();
```

```python
import requests

def get_finalized_head():
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getFinalizedHead',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

finalized_hash = get_finalized_head()
print(f'Finalized block hash: {finalized_hash}')

# chain_getFinalizedHead - Bridge Hub RPC Method
payload = {
    'jsonrpc': '2.0',
    'method': 'chain_getBlock',
    'params': [finalized_hash],
    'id': 2
}

response = requests.post('https://bridge-hub-polkadot-rpc.n.dwellir.com', json=payload)
block = response.json()['result']
block_number = int(block['block']['header']['number'], 16)
print(f'Finalized block number: {block_number}')
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let finalized_hash = api.rpc()
        .chain_get_finalized_head()
        .await?;

    println!("Finalized block hash: {:?}", finalized_hash);

    let block = api.rpc()
        .chain_get_block(Some(finalized_hash))
        .await?
        .expect("Finalized block should exist");

    println!("Finalized block number: {}", block.block.header.number);

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Confirmation

Wait for finality before crediting deposits on Bridge Hub:

```javascript
async function waitForFinality(api, txBlockHash) {
  return new Promise((resolve) => {
    const unsub = api.rpc.chain.subscribeFinalizedHeads(async (header) => {
      const finalizedHash = await api.rpc.chain.getBlockHash(header.number);

      // Check if the transaction block has been finalized
      const finalizedNumber = header.number.toNumber();
      const txBlock = await api.rpc.chain.getBlock(txBlockHash);
      const txNumber = txBlock.block.header.number.toNumber();

      if (finalizedNumber >= txNumber) {
        console.log(`Transaction finalized at block #${txNumber}`);
        unsub();
        resolve(txBlockHash);
      }
    });
  });
}
```

### 2. Safe State Queries

Query chain state at the finalized block to avoid reading data that could be reverted:

```javascript
async function getSafeBalance(api, address) {
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const apiAt = await api.at(finalizedHash);
  const account = await apiAt.query.system.account(address);

  return {
    free: account.data.free.toString(),
    reserved: account.data.reserved.toString(),
    finalizedAt: finalizedHash.toHex()
  };
}
```

### 3. Finality Lag Monitor

Track the gap between best and finalized blocks for health monitoring:

```javascript
async function monitorFinalityLag(api, threshold = 10) {
  const bestHeader = await api.rpc.chain.getHeader();
  const finalizedHash = await api.rpc.chain.getFinalizedHead();
  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);

  const lag = bestHeader.number.toNumber() - finalizedHeader.number.toNumber();
  console.log(`Finality lag: ${lag} blocks`);

  if (lag > threshold) {
    console.warn(`WARNING: Finality lag (${lag}) exceeds threshold (${threshold})`);
  }

  return lag;
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) - Get full block data by hash
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bridge-hub/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers
- [`grandpa_roundState`](https://www.dwellir.com/docs/bridge-hub/grandpa_roundState) - Monitor GRANDPA finality progress

---

## chain_getHeader - Bridge Hub RPC Method

Returns the block header for a given hash on Bridge Hub. This is a lightweight alternative to `chain_getBlock` when you only need header metadata without extrinsic data.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`chain_getHeader` is ideal for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Lightweight Queries** - Get block metadata without downloading full extrinsic data on Bridge Hub
- **Chain Synchronization** - Track block production and monitor chain progress for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Parent Chain Navigation** - Follow `parentHash` links to traverse the chain backwards
- **State Verification** - Use `stateRoot` and `extrinsicsRoot` for Merkle proof verification

## Best Practices

- Headers are much smaller than full blocks -- use for quick verification without body data
- The `parentHash` field verifies chain continuity by linking to the previous block
- Digest logs contain consensus messages and seal data
- Cache headers for recent blocks to reduce repeated API calls

## Request Parameters

- `blockHash` (`String, optional`): Hex-encoded block hash. If omitted, returns the latest block header

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_getHeader",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Hash of the parent block
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): Merkle root of the state trie after this block
- `extrinsicsRoot` (`Hash, required`): Merkle root of the extrinsics trie
- `digest` (`Digest, required`): Block digest containing consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "parentHash": "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
    "number": "0xf4240",
    "stateRoot": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "extrinsicsRoot": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "digest": {
      "logs": [
        "0x0642414245b50103..."
      ]
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid block hash"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_getHeader - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": [],
    "id": 1
  }'

# Get header for a specific block hash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "chain_getHeader",
    "params": ["0xYOUR_RECENT_BLOCK_HASH"],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get latest header
const header = await api.rpc.chain.getHeader();
console.log('Block number:', header.number.toNumber());
console.log('Parent hash:', header.parentHash.toHex());
console.log('State root:', header.stateRoot.toHex());
console.log('Extrinsics root:', header.extrinsicsRoot.toHex());

// Get header for a specific block hash
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalHeader = await api.rpc.chain.getHeader(blockHash);
console.log('Block #1000000 parent:', historicalHeader.parentHash.toHex());

await api.disconnect();
```

```python
import requests

def get_header(block_hash=None):
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'chain_getHeader',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

# Get latest header
header = get_header()
block_number = int(header['number'], 16)
print(f'Block number: {block_number}')
print(f"Parent hash: {header['parentHash']}")
print(f"State root: {header['stateRoot']}")
print(f"Extrinsics root: {header['extrinsicsRoot']}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    // Get latest header
    let header = api.rpc()
        .chain_get_header(None)
        .await?
        .expect("Header should exist");

    println!("Block number: {}", header.number);
    println!("Parent hash: {:?}", header.parent_hash);
    println!("State root: {:?}", header.state_root);

    Ok(())
}
```

## Common Use Cases

### 1. Block Time Calculator

Estimate block production rate on Bridge Hub:

```javascript
async function estimateBlockTime(api, sampleSize = 10) {
  const latestHeader = await api.rpc.chain.getHeader();
  const latestNumber = latestHeader.number.toNumber();

  const oldHash = await api.rpc.chain.getBlockHash(latestNumber - sampleSize);
  const oldHeader = await api.rpc.chain.getHeader(oldHash);

  // Use timestamp from block digests or timestamp pallet
  const latestTimestamp = await api.query.timestamp.now();
  const apiAt = await api.at(oldHash);
  const oldTimestamp = await apiAt.query.timestamp.now();

  const timeDiff = latestTimestamp.toNumber() - oldTimestamp.toNumber();
  const avgBlockTime = timeDiff / sampleSize;

  console.log(`Average block time: ${avgBlockTime / 1000}s over ${sampleSize} blocks`);
  return avgBlockTime;
}
```

### 2. Chain Traversal

Walk backwards through the Bridge Hub chain using parent hashes:

```javascript
async function walkChain(api, startHash, depth = 5) {
  let currentHash = startHash || (await api.rpc.chain.getBlockHash());
  const headers = [];

  for (let i = 0; i < depth; i++) {
    const header = await api.rpc.chain.getHeader(currentHash);
    headers.push({
      number: header.number.toNumber(),
      hash: currentHash.toString(),
      parentHash: header.parentHash.toHex()
    });
    currentHash = header.parentHash;
  }

  return headers;
}
```

### 3. Lightweight Block Monitor

Monitor Bridge Hub block production without downloading full blocks:

```javascript
async function monitorBlocks(api, callback) {
  let lastNumber = 0;

  setInterval(async () => {
    const header = await api.rpc.chain.getHeader();
    const number = header.number.toNumber();

    if (number > lastNumber) {
      console.log(`New block #${number}`);
      callback(header);
      lastNumber = number;
    }
  }, 3000);
}
```

## Related Methods

- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) - Get full block with extrinsics
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bridge-hub/chain_getBlockHash) - Get block hash by number
- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeNewHeads) - Subscribe to new block headers in real time
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized block headers

---

## chain_subscribeFinalizedHeads - Bridge Hub RPC Method

Subscribe to receive notifications when blocks are finalized on Bridge Hub. Finalized blocks are guaranteed to never be reverted by the GRANDPA finality gadget, making this the safest way to track confirmed state changes.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`chain_subscribeFinalizedHeads` is critical for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Exchange Deposits** - Only credit funds after finalization for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Bridge Operations** - Wait for finality before executing cross-chain transfers
- **Critical State Changes** - Ensure irreversibility before acting on important transactions
- **Compliance Workflows** - Record-keeping that requires provably irreversible state

## Best Practices

- Requires a WebSocket connection at `wss://bridge-hub-polkadot-rpc.n.dwellir.com`
- Finalized headers are irreversible and safe for bridge relay operations
- Notification frequency is lower than `chain_subscribeNewHeads`
- Unsubscribe when done to free connection resources

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeFinalizedHeads - Bridge Hub RPC Method
wscat -c wss://bridge-hub-polkadot-rpc.n.dwellir.com -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeFinalizedHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Subscribe to finalized heads
const unsubscribe = await api.rpc.chain.subscribeFinalizedHeads((header) => {
  console.log(`Finalized block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_finalized():
    uri = 'wss://bridge-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeFinalizedHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for finalized headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f'Finalized: #{block_num}')
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_finalized())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let mut finalized_heads = api.rpc()
        .subscribe_finalized_block_headers()
        .await?;

    while let Some(Ok(header)) = finalized_heads.next().await {
        println!(
            "Finalized block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Exchange Deposit Watcher

Watch for finalized transfers and credit user accounts on Bridge Hub:

```javascript
async function watchDeposits(api, depositAddresses) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const blockHash = header.hash;
    const block = await api.rpc.chain.getBlock(blockHash);
    const apiAt = await api.at(blockHash);
    const events = await apiAt.query.system.events();

    // Check for transfer events in the finalized block
    events.forEach((record) => {
      const { event } = record;
      if (event.section === 'balances' && event.method === 'Transfer') {
        const [from, to, amount] = event.data;
        if (depositAddresses.includes(to.toString())) {
          console.log(`Finalized deposit: ${amount} from ${from} to ${to}`);
          // Credit user account - this block will never be reverted
        }
      }
    });
  });

  return unsub;
}
```

### 2. Finality Lag Tracker

Monitor the gap between best and finalized blocks:

```javascript
async function trackFinalityLag(api) {
  let bestNumber = 0;

  api.rpc.chain.subscribeNewHeads((header) => {
    bestNumber = header.number.toNumber();
  });

  api.rpc.chain.subscribeFinalizedHeads((header) => {
    const finalizedNumber = header.number.toNumber();
    const lag = bestNumber - finalizedNumber;

    console.log(`Best: #${bestNumber} | Finalized: #${finalizedNumber} | Lag: ${lag} blocks`);

    if (lag > 10) {
      console.warn('WARNING: High finality lag detected - GRANDPA may be stalling');
    }
  });
}
```

### 3. Cross-Chain Bridge Relay

Relay finalized headers to a bridge contract:

```javascript
async function relayFinalizedHeaders(api, bridgeContract) {
  const unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => {
    const headerData = {
      number: header.number.toNumber(),
      stateRoot: header.stateRoot.toHex(),
      extrinsicsRoot: header.extrinsicsRoot.toHex(),
      parentHash: header.parentHash.toHex()
    };

    console.log(`Relaying finalized header #${headerData.number}`);
    await bridgeContract.submitHeader(headerData);
  });

  return unsub;
}
```

## Finality Lag

Finalized blocks typically lag behind the best block by a few blocks due to GRANDPA consensus requirements. This lag is normal and ensures Byzantine fault tolerance. The typical lag is 2-3 blocks under healthy network conditions.

## Related Methods

- [`chain_subscribeNewHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeNewHeads) - Subscribe to all new blocks (not just finalized)
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) - Get current finalized block hash (one-shot)
- [`grandpa_roundState`](https://www.dwellir.com/docs/bridge-hub/grandpa_roundState) - Monitor GRANDPA consensus progress
- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) - Get full block data for a finalized hash

---

## chain_subscribeNewHeads - Bridge Hub RPC Method

Subscribe to receive notifications when new block headers are produced on Bridge Hub. This WebSocket subscription provides real-time, push-based updates for each new block, making it more efficient than polling.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`chain_subscribeNewHeads` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Block Monitoring** - Track new blocks in real time on Bridge Hub for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Event Indexing** - Trigger processing pipelines when new blocks arrive
- **Chain Synchronization** - Keep external databases and systems in sync with the chain
- **Dashboard Updates** - Push live block data to monitoring dashboards

## Best Practices

- Requires a WebSocket connection at `wss://bridge-hub-polkadot-rpc.n.dwellir.com`
- Unsubscribe when monitoring is no longer needed to free node resources
- Headers arrive faster than full blocks -- use `chain_getBlock` for full data when needed
- For consensus-critical applications, prefer `chain_subscribeFinalizedHeads`

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}
```

## Response Fields

- `parentHash` (`Hash, required`): Parent block hash
- `number` (`BlockNumber, required`): Block number (hex-encoded)
- `stateRoot` (`Hash, required`): State trie root hash after this block
- `extrinsicsRoot` (`Hash, required`): Extrinsics trie root hash
- `digest` (`Digest, required`): Block digest with consensus engine logs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "subscription_id_here"
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# chain_subscribeNewHeads - Bridge Hub RPC Method
wscat -c wss://bridge-hub-polkadot-rpc.n.dwellir.com -x '{
  "jsonrpc": "2.0",
  "method": "chain_subscribeNewHeads",
  "params": [],
  "id": 1
}'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Subscribe to new heads
const unsubscribe = await api.rpc.chain.subscribeNewHeads((header) => {
  console.log(`New block #${header.number}`);
  console.log(`  Hash: ${header.hash.toHex()}`);
  console.log(`  Parent: ${header.parentHash.toHex()}`);
  console.log(`  State root: ${header.stateRoot.toHex()}`);
  console.log(`  Extrinsics root: ${header.extrinsicsRoot.toHex()}`);
});

// Unsubscribe after 60 seconds
setTimeout(() => {
  unsubscribe();
  api.disconnect();
}, 60000);
```

```python
import asyncio
import websockets
import json

async def subscribe_new_heads():
    uri = 'wss://bridge-hub-polkadot-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Subscribe to new heads
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'chain_subscribeNewHeads',
            'params': [],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        sub_id = response['result']
        print(f'Subscribed with ID: {sub_id}')

        # Listen for new headers
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                header = message['params']['result']
                block_num = int(header['number'], 16)
                print(f"Block #{block_num}")
                print(f"  Parent: {header['parentHash']}")
                print(f"  State root: {header['stateRoot']}")

asyncio.run(subscribe_new_heads())
```

```rust
use subxt::{OnlineClient, PolkadotConfig};
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let mut new_heads = api.rpc()
        .subscribe_all_block_headers()
        .await?;

    while let Some(Ok(header)) = new_heads.next().await {
        println!(
            "New block #{}: {:?}",
            header.number,
            header.hash()
        );
    }

    Ok(())
}
```

## Common Use Cases

### 1. Real-Time Block Indexer

Index new blocks and their events on Bridge Hub as they arrive:

```javascript
async function indexBlocks(api, onBlock) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const blockHash = header.hash;
    const [block, events] = await Promise.all([
      api.rpc.chain.getBlock(blockHash),
      api.query.system.events.at(blockHash)
    ]);

    const blockData = {
      number: header.number.toNumber(),
      hash: blockHash.toHex(),
      parentHash: header.parentHash.toHex(),
      extrinsicCount: block.block.extrinsics.length,
      eventCount: events.length,
      timestamp: Date.now()
    };

    await onBlock(blockData);
  });

  return unsub;
}
```

### 2. Block Production Monitor

Detect block production delays on Bridge Hub:

```javascript
async function monitorBlockProduction(api, expectedBlockTimeMs = 6000) {
  let lastBlockTime = Date.now();
  const threshold = expectedBlockTimeMs * 3;

  const unsub = await api.rpc.chain.subscribeNewHeads((header) => {
    const now = Date.now();
    const elapsed = now - lastBlockTime;

    if (elapsed > threshold) {
      console.warn(
        `Block #${header.number}: ${elapsed}ms since last block (expected ~${expectedBlockTimeMs}ms)`
      );
    } else {
      console.log(`Block #${header.number}: ${elapsed}ms`);
    }

    lastBlockTime = now;
  });

  return unsub;
}
```

### 3. Live Dashboard Feed

Stream block data to a WebSocket-connected frontend:

```javascript
async function streamToClients(api, wss) {
  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const message = JSON.stringify({
      type: 'new_block',
      number: header.number.toNumber(),
      hash: header.hash.toHex(),
      parentHash: header.parentHash.toHex(),
      stateRoot: header.stateRoot.toHex()
    });

    wss.clients.forEach((client) => {
      if (client.readyState === 1) {
        client.send(message);
      }
    });
  });

  return unsub;
}
```

## Subscription vs Polling

| Approach            | Latency                    | Resource Usage             | Use Case                       |
| ------------------- | -------------------------- | -------------------------- | ------------------------------ |
| `subscribeNewHeads` | Immediate                  | Low (push-based)           | Real-time monitoring, indexing |
| Polling `getHeader` | Block time + poll interval | Higher (repeated requests) | Simple integrations, HTTP-only |

## Related Methods

- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) - Subscribe to finalized blocks only (for irreversible state)
- [`chain_getHeader`](https://www.dwellir.com/docs/bridge-hub/chain_getHeader) - Get a specific block header by hash
- [`chain_getBlock`](https://www.dwellir.com/docs/bridge-hub/chain_getBlock) - Get full block data with extrinsics
- `chain_unsubscribeNewHeads` - Unsubscribe from new heads

---

## grandpa_roundState - Bridge Hub RPC Method

Returns the state of the current GRANDPA finality round on Bridge Hub when the endpoint exposes validator-round internals. GRANDPA (GHOST-based Recursive ANcestor Deriving Prefix Agreement) is the finality gadget used by many Substrate-based chains to provide deterministic finality, but some public endpoints do not surface `grandpa_roundState` and instead return a method-not-found style error.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`grandpa_roundState` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Finality Monitoring** -- Track whether GRANDPA rounds are progressing normally or stalling on Bridge Hub, critical for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Consensus Health Checks** -- Detect finality delays by comparing prevote/precommit counts against the supermajority threshold weight
- **Validator Participation Analysis** -- Monitor which validators are actively voting and whether the authority set has sufficient online weight
- **Authority Set Tracking** -- Observe `setId` changes after validator set rotations to verify smooth authority transitions
- **Capability Detection** -- Confirm whether the shared endpoint exposes GRANDPA round internals before you build monitoring around them

## Best Practices

- Primarily used for network monitoring and consensus debugging
- Returns `prevotes` and `precommits` from active validators
- Response may be large on networks with many validators
- Most applications should use `chain_getFinalizedHead` instead for finality tracking

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "grandpa_roundState",
  "params": [],
  "id": 1
}
```

## Response Fields

- `setId` (`u64, required`): The current GRANDPA authority set ID; increments when the validator set changes
- `best` (`RoundState, required`): State of the best (most recent) active round
- `background` (`Vec<RoundState>, required`): Background rounds that are still being tracked (typically the previous round)
- `round` (`u64, required`): The round number
- `totalWeight` (`u64, required`): Total combined weight of all authorities in this set
- `thresholdWeight` (`u64, required`): Minimum weight required for a supermajority (2/3 + 1 of totalWeight)
- `prevotes` (`Votes, required`): Current prevote state for this round
- `precommits` (`Votes, required`): Current precommit state for this round
- `currentWeight` (`u64, required`): Total weight of votes received so far
- `missing` (`Vec<AuthorityId>, required`): List of authority public keys that have not yet voted

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "setId": 4821,
    "best": {
      "round": 19384,
      "totalWeight": 297,
      "thresholdWeight": 199,
      "prevotes": {
        "currentWeight": 297,
        "missing": []
      },
      "precommits": {
        "currentWeight": 264,
        "missing": [
          "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
          "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
        ]
      }
    },
    "background": []
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "grandpa_roundState",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

try {
  const roundState = await api.rpc.grandpa.roundState();
  const best = roundState.best;

  console.log('Authority set ID:', roundState.setId.toString());
  console.log('Round:', best.round.toString());
  console.log('Total weight:', best.totalWeight.toString());
  console.log('Threshold weight:', best.thresholdWeight.toString());
  console.log('Prevote weight:', best.prevotes.currentWeight.toString());
  console.log('Precommit weight:', best.precommits.currentWeight.toString());
  console.log('Missing precommits:', best.precommits.missing.length);
} catch (error) {
  console.log('grandpa_roundState unsupported:', error.message);
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'grandpa_roundState',
    params: [],
    id: 1
  })
});

const payload = await response.json();
if (payload.error) {
  console.log('grandpa_roundState unsupported:', payload.error.message);
} else {
  console.log('Set ID:', payload.result.setId);
  console.log('Best round:', payload.result.best.round);
  console.log('Prevote progress:', payload.result.best.prevotes.currentWeight, '/', payload.result.best.thresholdWeight);
  console.log('Precommit progress:', payload.result.best.precommits.currentWeight, '/', payload.result.best.thresholdWeight);
}
```

```python
import requests

def get_grandpa_round_state():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'grandpa_roundState',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

try:
    state = get_grandpa_round_state()
    best = state['best']

    print(f"Authority set ID: {state['setId']}")
    print(f"Round: {best['round']}")
    print(f"Prevote: {best['prevotes']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Precommit: {best['precommits']['currentWeight']}/{best['thresholdWeight']}")
    print(f"Missing precommit voters: {len(best['precommits']['missing'])}")
except KeyError:
    print('grandpa_roundState unsupported on this endpoint')

# grandpa_roundState - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
response = substrate.rpc_request('grandpa_roundState', [])
if 'error' in response:
    print(f"grandpa_roundState unsupported: {response['error']['message']}")
else:
    print(f"Set ID: {response['result']['setId']}, Round: {response['result']['best']['round']}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RoundState {
    set_id: u64,
    best: BestRound,
    background: Vec<BestRound>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct BestRound {
    round: u64,
    total_weight: u64,
    threshold_weight: u64,
    prevotes: Votes,
    precommits: Votes,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Votes {
    current_weight: u64,
    missing: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "grandpa_roundState",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    if body.get("error").is_some() {
        println!("grandpa_roundState unsupported: {}", body["error"]["message"]);
        return Ok(());
    }

    let state: RoundState = serde_json::from_value(body["result"].clone())?;

    println!("Set ID: {}", state.set_id);
    println!("Round: {}", state.best.round);
    println!("Prevote: {}/{}", state.best.prevotes.current_weight, state.best.threshold_weight);
    println!("Precommit: {}/{}", state.best.precommits.current_weight, state.best.threshold_weight);
    println!("Missing precommit voters: {}", state.best.precommits.missing.len());

    Ok(())
}
```

## Common Use Cases

### 1. Finality Health Monitoring

Periodically check whether GRANDPA rounds are progressing and alert on stalls:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorFinality(api, intervalMs = 10000) {
  let lastRound = 0;
  let lastSetId = 0;
  let stallCount = 0;

  setInterval(async () => {
    const state = await api.rpc.grandpa.roundState();
    const best = state.best;
    const round = best.round.toNumber();
    const setId = state.setId.toNumber();
    const prevoteProgress = best.prevotes.currentWeight.toNumber();
    const precommitProgress = best.precommits.currentWeight.toNumber();
    const threshold = best.thresholdWeight.toNumber();

    if (setId !== lastSetId) {
      console.log(`Authority set changed: ${lastSetId} -> ${setId}`);
      lastSetId = setId;
    }

    if (round === lastRound) {
      stallCount++;
      if (stallCount >= 3) {
        console.warn(`GRANDPA round ${round} stalled for ${stallCount} checks`);
        console.warn(`  Prevotes: ${prevoteProgress}/${threshold}`);
        console.warn(`  Precommits: ${precommitProgress}/${threshold}`);
        console.warn(`  Missing voters: ${best.precommits.missing.length}`);
      }
    } else {
      stallCount = 0;
      console.log(`Round ${round} | prevotes=${prevoteProgress}/${threshold} precommits=${precommitProgress}/${threshold}`);
    }

    lastRound = round;
  }, intervalMs);
}
```

### 2. Validator Participation Report

Generate a report of which validators are consistently missing votes:

```javascript
async function trackMissingVoters(api, samples = 20, delayMs = 6000) {
  const missingCounts = {};

  for (let i = 0; i < samples; i++) {
    const state = await api.rpc.grandpa.roundState();
    const missing = state.best.precommits.missing;

    missing.forEach((authority) => {
      const key = authority.toString();
      missingCounts[key] = (missingCounts[key] || 0) + 1;
    });

    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }

  // Sort by most frequently missing
  const sorted = Object.entries(missingCounts)
    .sort(([, a], [, b]) => b - a);

  console.log('Validator participation report:');
  sorted.forEach(([authority, count]) => {
    const missRate = ((count / samples) * 100).toFixed(1);
    console.log(`  ${authority}: missed ${count}/${samples} (${missRate}%)`);
  });

  return sorted;
}
```

### 3. Supported-Fallback Check

If the endpoint does not expose GRANDPA round internals, fall back to finalized-head tracking:

```javascript
async function getFinalitySignal(api) {
  try {
    return { supported: true, roundState: await api.rpc.grandpa.roundState() };
  } catch (error) {
    return {
      supported: false,
      finalizedHead: (await api.rpc.chain.getFinalizedHead()).toHex(),
      message: error.message
    };
  }
}
```

### 3. Finality Lag Detection

Compare the finalized head with the best block to measure finality lag:

```javascript
async function getFinalityLag(api) {
  const [roundState, finalizedHash, bestHeader] = await Promise.all([
    api.rpc.grandpa.roundState(),
    api.rpc.chain.getFinalizedHead(),
    api.rpc.chain.getHeader()
  ]);

  const finalizedHeader = await api.rpc.chain.getHeader(finalizedHash);
  const bestNumber = bestHeader.number.toNumber();
  const finalizedNumber = finalizedHeader.number.toNumber();
  const lag = bestNumber - finalizedNumber;

  return {
    bestBlock: bestNumber,
    finalizedBlock: finalizedNumber,
    lagBlocks: lag,
    grandpaRound: roundState.best.round.toNumber(),
    setId: roundState.setId.toNumber(),
    prevoteReached: roundState.best.prevotes.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber(),
    precommitReached: roundState.best.precommits.currentWeight.toNumber() >= roundState.best.thresholdWeight.toNumber()
  };
}
```

## Understanding GRANDPA Rounds

GRANDPA achieves finality through a two-phase voting protocol:

1. **Prevote Phase** -- Each authority broadcasts a prevote for the highest block they consider best. Once prevotes reach the `thresholdWeight` (supermajority), the protocol derives the highest block that is an ancestor of all supermajority prevotes.

2. **Precommit Phase** -- Authorities that observe a supermajority of prevotes issue precommits for the block derived in the prevote phase. When precommits reach the threshold, that block and all its ancestors are finalized.

3. **Authority Sets** -- The `setId` increments each time the authority set changes (e.g., after a session rotation). A new authority set starts a new round sequence from round 1.

| Concept             | Description                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------- |
| **totalWeight**     | Sum of all authority weights in the current set                                               |
| **thresholdWeight** | `⌊totalWeight × 2/3⌋ + 1` -- minimum for supermajority                                        |
| **Healthy round**   | `prevotes.currentWeight >= thresholdWeight` AND `precommits.currentWeight >= thresholdWeight` |
| **Stalled round**   | Neither prevotes nor precommits reach threshold for an extended period                        |

## Related Methods

- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/chain_getFinalizedHead) -- Get the hash of the latest finalized block
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) -- Subscribe to new finalized block headers
- `grandpa_proveFinality` -- Get a finality proof for a specific block number
- [`beefy_getFinalizedHead`](https://www.dwellir.com/docs/bridge-hub/beefy_getFinalizedHead) -- Get the latest BEEFY finalized block (if BEEFY is enabled)
- [`system_health`](https://www.dwellir.com/docs/bridge-hub/system_health) -- Check overall node health including sync and peer status

---

## payment_queryFeeDetails - Bridge Hub RPC Method

Returns a detailed breakdown of the inclusion fee for a given extrinsic on Bridge Hub. While `payment_queryInfo` returns the total fee as a single value, this method separates it into three components: the fixed base fee, the length-proportional fee, and the weight-based adjusted fee. This granularity is essential for understanding and optimizing transaction costs.

If you provide `blockHash`, it must be a real chain block hash. Placeholder hashes and stale examples return an `unknown Block` style error.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`payment_queryFeeDetails` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Fee Optimization** -- Identify which fee component dominates your transaction cost and optimize accordingly on Bridge Hub
- **Transaction Cost Analysis** -- Build detailed cost breakdowns for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers, showing users exactly where their fees go
- **Fee Model Comparison** -- Compare fee structures across different extrinsic types or between runtime upgrades that change fee parameters
- **Batching Decisions** -- Determine whether batching calls saves fees by amortizing the base fee across multiple operations

## Best Practices

- Returns `baseFee`, `lenFee`, and `adjustedWeightFee` for detailed cost analysis
- More granular than `payment_queryInfo` -- useful for gas optimization
- Fee components are calculated from weight and length of the extrinsic
- Weight-adjusted fees may vary based on current network congestion

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized extrinsic (signed or unsigned)
- `blockHash` (`String, optional`): Block hash at which to calculate fees; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryFeeDetails",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `inclusionFee` (`Option<InclusionFee>, required`): Fee breakdown object, or null if the extrinsic does not pay fees
- `baseFee` (`String, required`): Fixed fee charged per extrinsic regardless of size or complexity (human-readable decimal string)
- `lenFee` (`String, required`): Fee proportional to the encoded byte length of the extrinsic (length * lengthToFee)
- `adjustedWeightFee` (`String, required`): Fee based on execution weight, adjusted by the current block fullness multiplier

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "inclusionFee": {
      "baseFee": "124414000000",
      "lenFee": "1430000000",
      "adjustedWeightFee": "2183055836"
    }
  }
}
```

## Error Responses

### Error Response

- Code: `-32602`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Could not decode extrinsic"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# payment_queryFeeDetails - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
    "id": 1
  }'

# Query fee details at a specific block
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryFeeDetails",
    "params": [
      "0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01...",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Create a sample transfer extrinsic
const tx = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Get fee details
const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

if (feeDetails.inclusionFee.isSome) {
  const fee = feeDetails.inclusionFee.unwrap();
  console.log('Base fee:', fee.baseFee.toString());
  console.log('Length fee:', fee.lenFee.toString());
  console.log('Weight fee:', fee.adjustedWeightFee.toString());

  const total = fee.baseFee.add(fee.lenFee).add(fee.adjustedWeightFee);
  console.log('Total inclusion fee:', total.toString());
}

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryFeeDetails',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  })
});

const { result } = await response.json();
if (result.inclusionFee) {
  console.log('Fee components:', result.inclusionFee);
}
```

```python
import requests

def query_fee_details(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryFeeDetails',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query fee details for an encoded extrinsic
encoded_extrinsic = '0x2d028400...'
result = query_fee_details(encoded_extrinsic)

if result['inclusionFee']:
    fee = result['inclusionFee']
    base = int(fee['baseFee'])
    length = int(fee['lenFee'])
    weight = int(fee['adjustedWeightFee'])
    total = base + length + weight

    print(f"Base fee:   {base:>20} planck")
    print(f"Length fee: {length:>20} planck")
    print(f"Weight fee: {weight:>20} planck")
    print(f"Total:      {total:>20} planck")
else:
    print('Extrinsic does not pay fees')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('payment_queryFeeDetails', [encoded_extrinsic])['result']
print(f"Fee details: {result}")
```

```rust
use serde_json::json;

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct FeeDetailsResponse {
    inclusion_fee: Option<InclusionFee>,
}

#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct InclusionFee {
    base_fee: String,
    len_fee: String,
    adjusted_weight_fee: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x2d028400...";

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryFeeDetails",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let body: serde_json::Value = response.json().await?;
    let details: FeeDetailsResponse = serde_json::from_value(body["result"].clone())?;

    match details.inclusion_fee {
        Some(fee) => {
            let base: u128 = fee.base_fee.parse()?;
            let len: u128 = fee.len_fee.parse()?;
            let weight: u128 = fee.adjusted_weight_fee.parse()?;
            let total = base + len + weight;

            println!("Base fee:   {:>20}", base);
            println!("Length fee: {:>20}", len);
            println!("Weight fee: {:>20}", weight);
            println!("Total:      {:>20}", total);
        }
        None => println!("Extrinsic does not pay fees"),
    }

    Ok(())
}
```

## Common Use Cases

### 1. Fee Component Analysis for Optimization

Analyze which fee component dominates to guide optimization strategies:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function analyzeFeeComponents(api, tx) {
  const feeDetails = await api.rpc.payment.queryFeeDetails(tx.toHex());

  if (feeDetails.inclusionFee.isNone) {
    return { feeless: true };
  }

  const fee = feeDetails.inclusionFee.unwrap();
  const base = BigInt(fee.baseFee.toString());
  const len = BigInt(fee.lenFee.toString());
  const weight = BigInt(fee.adjustedWeightFee.toString());
  const total = base + len + weight;

  const analysis = {
    baseFee: { value: base, percentage: Number((base * 10000n) / total) / 100 },
    lenFee: { value: len, percentage: Number((len * 10000n) / total) / 100 },
    weightFee: { value: weight, percentage: Number((weight * 10000n) / total) / 100 },
    total
  };

  // Suggest optimization based on dominant component
  if (analysis.lenFee.percentage > 50) {
    analysis.suggestion = 'Length fee dominates -- reduce call data size or batch smaller calls';
  } else if (analysis.weightFee.percentage > 50) {
    analysis.suggestion = 'Weight fee dominates -- choose lighter runtime operations';
  } else {
    analysis.suggestion = 'Fees are balanced -- batch calls to amortize base fee';
  }

  return analysis;
}
```

### 2. Batch vs. Individual Fee Comparison

Compare the cost of batching calls versus submitting them individually:

```javascript
async function compareBatchVsIndividual(api, calls) {
  // Individual fee total
  let individualTotal = 0n;
  for (const call of calls) {
    const tx = api.tx(call);
    const details = await api.rpc.payment.queryFeeDetails(tx.toHex());
    if (details.inclusionFee.isSome) {
      const fee = details.inclusionFee.unwrap();
      individualTotal += BigInt(fee.baseFee.toString())
        + BigInt(fee.lenFee.toString())
        + BigInt(fee.adjustedWeightFee.toString());
    }
  }

  // Batched fee
  const batchTx = api.tx.utility.batchAll(calls);
  const batchDetails = await api.rpc.payment.queryFeeDetails(batchTx.toHex());
  let batchTotal = 0n;
  if (batchDetails.inclusionFee.isSome) {
    const fee = batchDetails.inclusionFee.unwrap();
    batchTotal = BigInt(fee.baseFee.toString())
      + BigInt(fee.lenFee.toString())
      + BigInt(fee.adjustedWeightFee.toString());
  }

  const savings = individualTotal - batchTotal;
  console.log(`Individual total: ${individualTotal} planck`);
  console.log(`Batch total:      ${batchTotal} planck`);
  console.log(`Savings:          ${savings} planck (${Number((savings * 10000n) / individualTotal) / 100}%)`);

  return { individualTotal, batchTotal, savings };
}
```

### 3. Fee Tracking Across Runtime Upgrades

Monitor how fee components change after runtime upgrades to detect regressions:

```javascript
async function compareFeesBetweenBlocks(api, extrinsicHex, blockHashBefore, blockHashAfter) {
  const [before, after] = await Promise.all([
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashBefore),
    api.rpc.payment.queryFeeDetails(extrinsicHex, blockHashAfter)
  ]);

  function extractFees(details) {
    if (details.inclusionFee.isNone) return null;
    const fee = details.inclusionFee.unwrap();
    return {
      base: BigInt(fee.baseFee.toString()),
      len: BigInt(fee.lenFee.toString()),
      weight: BigInt(fee.adjustedWeightFee.toString())
    };
  }

  const feesBefore = extractFees(before);
  const feesAfter = extractFees(after);

  if (feesBefore && feesAfter) {
    console.log('Fee comparison:');
    console.log(`  Base fee:   ${feesBefore.base} -> ${feesAfter.base}`);
    console.log(`  Length fee: ${feesBefore.len} -> ${feesAfter.len}`);
    console.log(`  Weight fee: ${feesBefore.weight} -> ${feesAfter.weight}`);
  }
}
```

## Fee Components Explained

| Component             | Source                | How It's Calculated                                                                      | Optimization Strategy                                                                     |
| --------------------- | --------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **baseFee**           | `ExtrinsicBaseWeight` | Fixed cost per extrinsic defined by the runtime                                          | Batch multiple calls into a single extrinsic to pay only one base fee                     |
| **lenFee**            | `TransactionByteFee`  | `encodedLength × lengthToFee` coefficient                                                | Minimize encoded extrinsic size by using compact encodings and avoiding large payloads    |
| **adjustedWeightFee** | `WeightToFee`         | Execution weight multiplied by the fee multiplier, which adjusts based on block fullness | Choose lighter operations, submit during low-traffic periods when the multiplier is lower |

**Tip multiplier**: The `adjustedWeightFee` is sensitive to network congestion. When blocks are consistently more than half full, the fee multiplier increases, raising the weight fee. During low-traffic periods, the multiplier decreases toward its minimum.

## Related Methods

- [`payment_queryInfo`](https://www.dwellir.com/docs/bridge-hub/payment_queryInfo) -- Get the total fee and execution weight for an extrinsic as a single value
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call `TransactionPaymentApi_query_fee_details` directly for more control
- [`system_properties`](https://www.dwellir.com/docs/bridge-hub/system_properties) -- Get token decimals and symbol for human-readable fee display
- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitExtrinsic) -- Submit the extrinsic after confirming acceptable fees
- [`author_submitAndWatchExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitAndWatchExtrinsic) -- Submit and track the extrinsic through finalization

---

## payment_queryInfo - Bridge Hub RPC Method

Estimates the fee for an encoded extrinsic on Bridge Hub. Returns the weight, dispatch class, and partial fee so you can display costs to users or verify sufficient balance before submitting transactions.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`payment_queryInfo` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Fee Display** -- Show users the estimated transaction cost before they sign on Bridge Hub
- **Balance Validation** -- Verify the sender has sufficient funds to cover the fee plus the transfer amount for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Transaction Planning** -- Compare fees across different extrinsic types to optimize costs
- **Batch Cost Estimation** -- Estimate the total cost of batch transactions before submission

## Best Practices

- Fees may change before extrinsic inclusion due to network conditions
- The `partialFee` is returned in planck (smallest unit of the native token)
- Test with actual encoded extrinsic data for the most accurate fee estimate
- Use `payment_queryFeeDetails` for a component-level fee breakdown

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded signed or unsigned extrinsic
- `blockHash` (`String, optional`): Block hash for fee calculation context; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "payment_queryInfo",
  "params": ["0x4d0284ff..."],
  "id": 1
}
```

## Response Fields

- `weight` (`Object, required`): The dispatch weight of the extrinsic, containing refTime (compute) and proofSize (storage proof)
- `class` (`String, required`): The dispatch class: "Normal", "Operational", or "Mandatory"
- `partialFee` (`String, required`): The estimated fee in the chain's smallest unit (e.g., Planck for Polkadot). Does not include tip

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "weight": {
      "refTime": 216215000,
      "proofSize": 3593
    },
    "class": "Normal",
    "partialFee": "157000152"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Unable to query dispatch info"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "payment_queryInfo",
    "params": ["0x4d0284ff..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Create a transfer extrinsic
const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const amount = 1000000000000; // Example base-unit amount; adjust for the chain's native decimals
const transfer = api.tx.balances.transferKeepAlive(recipient, amount);

// Query fee info using a sender address
const sender = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const info = await transfer.paymentInfo(sender);

console.log('Partial fee:', info.partialFee.toHuman());
console.log('Weight:', info.weight.toString());
console.log('Class:', info.class.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a pre-encoded extrinsic
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'payment_queryInfo',
    params: [transfer.toHex()],
    id: 1
  })
});

const { result } = await response.json();
console.log('Fee estimate:', result.partialFee);
```

```python
import requests

def query_fee_info(extrinsic_hex, block_hash=None):
    params = [extrinsic_hex]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'payment_queryInfo',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# payment_queryInfo - Bridge Hub RPC Method
extrinsic_hex = '0x4d0284ff...'
info = query_fee_info(extrinsic_hex)
print(f"Partial fee: {info['partialFee']}")
print(f"Weight: {info['weight']}")
print(f"Class: {info['class']}")

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')

# Build a transfer call
call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
        'value': 1000000000000
    }
)

# Create extrinsic for fee estimation
keypair = Keypair.create_from_uri('//Alice')
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
info = substrate.get_payment_info(call=call, keypair=keypair)
print(f"Estimated fee: {info['partialFee']}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DispatchInfo {
    weight: Weight,
    class: String,
    partial_fee: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Weight {
    ref_time: u64,
    proof_size: u64,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let extrinsic_hex = "0x4d0284ff..."; // pre-encoded extrinsic

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "payment_queryInfo",
            "params": [extrinsic_hex],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let info: DispatchInfo = serde_json::from_value(result["result"].clone())?;

    println!("Partial fee: {}", info.partial_fee);
    println!("Weight: refTime={}, proofSize={}", info.weight.ref_time, info.weight.proof_size);
    println!("Class: {}", info.class);
    Ok(())
}
```

## Common Use Cases

### 1. Pre-Transaction Fee Display

Show fees to users before they confirm a transaction:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function displayFeeEstimate(api, extrinsic, senderAddress) {
  const [info, properties] = await Promise.all([
    extrinsic.paymentInfo(senderAddress),
    api.rpc.system.properties()
  ]);

  const decimals = properties.tokenDecimals.toJSON()[0];
  const symbol = properties.tokenSymbol.toJSON()[0];
  const fee = info.partialFee;

  // Convert to human-readable
  const divisor = new BN(10).pow(new BN(decimals));
  const whole = fee.div(divisor);
  const fractional = fee.mod(divisor).toString().padStart(decimals, '0');

  const formatted = `${whole}.${fractional.slice(0, 6)} ${symbol}`;
  console.log(`Estimated fee: ${formatted}`);
  console.log(`Dispatch class: ${info.class.toString()}`);

  return { fee: fee.toString(), formatted, class: info.class.toString() };
}
```

### 2. Sufficient Balance Check

Verify the sender can afford the transaction plus fees:

```javascript
async function canAffordTransaction(api, senderAddress, recipient, amount) {
  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const [info, account] = await Promise.all([
    transfer.paymentInfo(senderAddress),
    api.query.system.account(senderAddress)
  ]);

  const fee = info.partialFee.toBigInt();
  const transferAmount = BigInt(amount);
  const totalCost = fee + transferAmount;
  const freeBalance = account.data.free.toBigInt();

  // Account for existential deposit
  const existentialDeposit = api.consts.balances.existentialDeposit.toBigInt();
  const available = freeBalance - existentialDeposit;

  const canAfford = available >= totalCost;

  console.log(`Free balance: ${freeBalance}`);
  console.log(`Total cost (amount + fee): ${totalCost}`);
  console.log(`Can afford: ${canAfford}`);

  return canAfford;
}
```

### 3. Compare Fees Across Transaction Types

Estimate fees for different operations to find the cheapest approach:

```javascript
async function compareFees(api, sender) {
  const recipient = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
  const amount = 1000000000000;

  // Different transaction types
  const extrinsics = {
    'transfer': api.tx.balances.transferKeepAlive(recipient, amount),
    'transferAll': api.tx.balances.transferAll(recipient, false),
    'batchTransfer': api.tx.utility.batchAll([
      api.tx.balances.transferKeepAlive(recipient, amount / 2),
      api.tx.balances.transferKeepAlive(recipient, amount / 2)
    ])
  };

  const fees = {};
  for (const [name, ext] of Object.entries(extrinsics)) {
    const info = await ext.paymentInfo(sender);
    fees[name] = {
      partialFee: info.partialFee.toHuman(),
      weight: info.weight.toString(),
      class: info.class.toString()
    };
  }

  console.table(fees);
  return fees;
}
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/bridge-hub/author_submitExtrinsic) -- Submit the extrinsic after verifying the fee
- [`payment_queryFeeDetails`](https://www.dwellir.com/docs/bridge-hub/payment_queryFeeDetails) -- Get a detailed fee breakdown (base fee, length fee, weight fee)
- [`system_properties`](https://www.dwellir.com/docs/bridge-hub/system_properties) -- Get token decimals and symbol for formatting the fee
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call `TransactionPaymentApi` directly for advanced fee queries
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/bridge-hub/author_pendingExtrinsics) -- Check pending extrinsics in the pool

---

## rpc_methods - Bridge Hub RPC Method

Returns a list of all RPC methods exposed by the Bridge Hub node. This is the definitive way to discover what methods are available on a given endpoint, including both standard Substrate methods and any custom chain-specific extensions.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`rpc_methods` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **API Discovery** -- Enumerate all available RPC methods to understand the full capabilities of a Bridge Hub node
- **Capability Detection** -- Check whether a specific method (e.g., `author_submitExtrinsic`, `state_call`) is available before calling it
- **Compatibility Testing** -- Verify that an endpoint supports the methods your application requires for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Tooling and Documentation** -- Auto-generate API references or client SDKs from the available method list

## Best Practices

- Call at application startup to discover available RPC capabilities
- Use to gate feature availability -- only call methods that appear in the returned list
- Method availability varies by node configuration and Substrate runtime version
- Verified: a standard Polkadot archive node exposes approximately 129 methods across all namespaces

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "rpc_methods",
  "params": [],
  "id": 1
}
```

## Response Fields

- `methods` (`Array<String>, required`): A sorted list of all available RPC method names

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "methods": [
      "author_pendingExtrinsics",
      "author_submitExtrinsic",
      "chain_getBlock",
      "chain_getBlockHash",
      "chain_getHeader",
      "payment_queryInfo",
      "rpc_methods",
      "state_call",
      "state_getKeysPaged",
      "state_getMetadata",
      "state_getStorage",
      "state_queryStorageAt",
      "system_chain",
      "system_name",
      "system_properties",
      "system_version"
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "rpc_methods",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const methods = await api.rpc.rpc.methods();
console.log('Available methods:', methods.methods.length);
methods.methods.forEach((m) => console.log(' -', m.toString()));

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'rpc_methods',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.methods.length} available methods`);
```

```python
import requests

def get_rpc_methods():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'rpc_methods',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']['methods']

methods = get_rpc_methods()
print(f'Available RPC methods ({len(methods)}):')
for method in methods:
    print(f'  - {method}')

# rpc_methods - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('rpc_methods', [])['result']
print(f"Methods: {len(result['methods'])}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct RpcMethodsResult {
    methods: Vec<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "rpc_methods",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let rpc: RpcMethodsResult = serde_json::from_value(result["result"].clone())?;

    println!("Available methods ({}):", rpc.methods.len());
    for method in &rpc.methods {
        println!("  - {}", method);
    }
    Ok(())
}
```

## Common Use Cases

### 1. Endpoint Capability Validation

Check whether a Bridge Hub endpoint supports all methods your application needs:

```javascript
async function validateEndpoint(endpoint, requiredMethods) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'rpc_methods',
      params: [],
      id: 1
    })
  });

  const { result } = await response.json();
  const available = new Set(result.methods);

  const missing = requiredMethods.filter((m) => !available.has(m));

  if (missing.length > 0) {
    console.error('Missing required methods:', missing);
    return false;
  }

  console.log('Endpoint supports all required methods');
  return true;
}

// Usage
await validateEndpoint('https://bridge-hub-polkadot-rpc.n.dwellir.com', [
  'state_getStorage',
  'state_call',
  'author_submitExtrinsic',
  'payment_queryInfo'
]);
```

### 2. Method Category Breakdown

Organize available methods by their RPC namespace:

```javascript
async function getMethodsByCategory(api) {
  const methods = await api.rpc.rpc.methods();
  const categories = {};

  methods.methods.forEach((method) => {
    const name = method.toString();
    const category = name.split('_')[0];
    categories[category] = categories[category] || [];
    categories[category].push(name);
  });

  for (const [category, methodList] of Object.entries(categories)) {
    console.log(`\n${category} (${methodList.length} methods):`);
    methodList.forEach((m) => console.log(`  - ${m}`));
  }

  return categories;
}
```

### 3. Compare Endpoints

Detect differences between two Bridge Hub endpoints:

```javascript
async function compareEndpoints(endpoint1, endpoint2) {
  const fetchMethods = async (url) => {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', method: 'rpc_methods', params: [], id: 1 })
    });
    const { result } = await res.json();
    return new Set(result.methods);
  };

  const [methods1, methods2] = await Promise.all([
    fetchMethods(endpoint1),
    fetchMethods(endpoint2)
  ]);

  const onlyIn1 = [...methods1].filter((m) => !methods2.has(m));
  const onlyIn2 = [...methods2].filter((m) => !methods1.has(m));

  if (onlyIn1.length) console.log('Only in endpoint 1:', onlyIn1);
  if (onlyIn2.length) console.log('Only in endpoint 2:', onlyIn2);
  if (!onlyIn1.length && !onlyIn2.length) console.log('Endpoints have identical methods');
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bridge-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get full runtime metadata including pallet and call definitions

---

## state_call - Bridge Hub RPC Method

Calls a runtime API function on Bridge Hub and returns the SCALE-encoded result. This method lets you execute runtime logic (such as `AccountNonceApi`, `TransactionPaymentApi`, or any custom runtime API) without submitting a transaction.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_call` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Account Nonce Queries** -- Retrieve the next nonce for an account via `AccountNonceApi_account_nonce` before constructing extrinsics
- **Fee Estimation** -- Use `TransactionPaymentApi_query_info` to estimate fees for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Custom Runtime APIs** -- Call any runtime API exposed by the chain (e.g., staking queries, governance lookups, DeFi calculations)
- **Historical State Queries** -- Execute runtime logic at a specific block by providing an optional block hash

## Best Practices

- Requires method name and encoded parameters specific to the runtime API
- Results are runtime-specific and version-dependent
- This is a non-mutating call -- safe for unlimited read queries
- Use `state_getRuntimeVersion` to verify compatibility before calling runtime APIs

## Request Parameters

- `method` (`String, required`): The runtime API method name (e.g., "AccountNonceApi_account_nonce")
- `data` (`String, required`): SCALE-encoded call data as a hex string (e.g., the encoded account ID)
- `blockHash` (`String, optional`): Block hash to execute against; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_call",
  "params": ["AccountNonceApi_account_nonce", "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): SCALE-encoded result as a hex string; decode with the appropriate codec for the runtime API return type

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x05000000"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Execution failed: Runtime API method not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_call - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_call",
    "params": [
      "AccountNonceApi_account_nonce",
      "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Call AccountNonceApi via the typed runtime API
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nonce = await api.call.accountNonceApi.accountNonce(account);
console.log('Account nonce:', nonce.toNumber());

// Call TransactionPaymentApi for fee estimation
const transfer = api.tx.balances.transferKeepAlive(account, 1000000000000);
const info = await api.call.transactionPaymentApi.queryInfo(transfer.toHex(), transfer.encodedLength);
console.log('Fee info:', info.toJSON());

await api.disconnect();

// Using fetch (low-level JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_call',
    params: [
      'AccountNonceApi_account_nonce',
      '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded result:', result);
```

```python
import requests

def state_call(method, data, block_hash=None):
    params = [method, data]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_call',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query account nonce
account_id = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = state_call('AccountNonceApi_account_nonce', account_id)
print(f'SCALE-encoded nonce: {result}')

# Using substrate-interface (auto-decodes)
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
nonce = substrate.rpc_request('state_call', [
    'AccountNonceApi_account_nonce',
    account_id
])['result']
print(f'Nonce result: {nonce}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Query account nonce via runtime API
    let account_id = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_call",
            "params": ["AccountNonceApi_account_nonce", account_id],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("SCALE-encoded nonce: {}", result["result"]);

    // Decode the SCALE-encoded u32 nonce
    let hex = result["result"].as_str().unwrap().trim_start_matches("0x");
    let bytes = hex::decode(hex)?;
    if bytes.len() >= 4 {
        let nonce = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
        println!("Decoded nonce: {}", nonce);
    }

    Ok(())
}
```

## Common Use Cases

### 1. Get Account Nonce for Transaction Construction

Query the next nonce before building and signing an extrinsic:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNextNonce(api, address) {
  // Using the runtime API directly (preferred over system.accountNextIndex)
  const nonce = await api.call.accountNonceApi.accountNonce(address);
  return nonce.toNumber();
}

async function buildAndSendTransfer(api, sender, recipient, amount) {
  const nonce = await getNextNonce(api, sender.address);

  const transfer = api.tx.balances.transferKeepAlive(recipient, amount);
  const hash = await transfer.signAndSend(sender, { nonce });

  console.log(`Sent with nonce ${nonce}, hash: ${hash.toHex()}`);
}
```

### 2. Custom Runtime API Queries

Call chain-specific runtime APIs for DeFi or governance queries:

```javascript
async function queryRuntimeApi(api, methodName, encodedArgs, blockHash) {
  const params = [methodName, encodedArgs];
  if (blockHash) params.push(blockHash);

  const result = await api.rpc.state.call(...params);
  return result.toHex();
}

// Example: query a staking-related runtime API at a specific block
const stakingResult = await queryRuntimeApi(
  api,
  'StakingApi_nominations_quota',
  '0x00e1f505', // SCALE-encoded balance
  '0xabc123...' // specific block hash
);
```

### 3. Historical State Query

Execute a runtime API call against a historical block:

```javascript
async function getNonceAtBlock(api, address, blockHash) {
  const nonce = await api.call.accountNonceApi.accountNonce.at(blockHash, address);
  return nonce.toNumber();
}

// Compare current nonce vs historical nonce
const currentNonce = await getNonceAtBlock(api, address);
const historicalNonce = await getNonceAtBlock(api, address, oldBlockHash);
console.log(`Transactions since block: ${currentNonce - historicalNonce}`);
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bridge-hub/state_getStorage) -- Query a single storage item by key
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get full runtime metadata including available runtime APIs
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bridge-hub/state_queryStorageAt) -- Batch query multiple storage keys at a specific block
- [`payment_queryInfo`](https://www.dwellir.com/docs/bridge-hub/payment_queryInfo) -- Estimate fees (uses `TransactionPaymentApi` internally)
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node version for compatibility checking

---

## state_getKeys - JSON-RPC Method

# state_getKeys - JSON-RPC Method

## Description

Returns storage keys that match a given prefix. This JSON-RPC method is useful for discovering all storage entries under a specific module or querying multiple related storage items. Be cautious with broad prefixes as they may return large result sets.

## Request Parameters

- `prefix` (`string, required`): Hex-encoded storage key prefix to match
- `blockHash` (`string, optional`): Block hash to query at. If omitted, uses the latest block

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "<prefix>",
    "<blockHash>"
  ],
  "id": 1
}
```

## Response Fields

- `result` (`array, required`): Array of hex-encoded storage keys matching the prefix

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"
  ],
  "id": 1
}
```

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeys",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9"
  ],
  "id": 1
}
```

## Code Examples

Python
JavaScript
TypeScript (@polkadot/api)

```python
import requests
import json
from substrateinterface import SubstrateInterface

def get_storage_keys(prefix, block_hash=None):
    url = "https://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY"
    headers = {
        "Content-Type": "application/json"
    }
    
    params = [prefix, block_hash] if block_hash else [prefix]
    
    payload = {
        "jsonrpc": "2.0",
        "method": "state_getKeys",
        "params": params,
        "id": 1
    }
    
    response = requests.post(url, headers=headers, data=json.dumps(payload))
    return response.json()["result"]

# Example: Get all validator preferences keys
def get_validator_keys():
    # Staking.Validators storage prefix
    prefix = "0x5f3e4907f716ac89b6347d15ececedca9320c2dc4f5d7af5b320b04e2d1a3ff3"
    keys = get_storage_keys(prefix)
    
    print(f"Found {len(keys)} validator preference entries")
    
    for key in keys:
        # Extract validator account from key
        validator_account = key[-64:]
        print(f"Validator: 0x{validator_account}")
    
    return keys

# Example: Query all keys under a module
def get_module_keys(module_prefix):
    keys = get_storage_keys(module_prefix)
    
    # Group keys by storage item
    storage_items = {}
    for key in keys:
        # Storage keys typically have a fixed prefix per item
        item_prefix = key[:66]  # First 33 bytes (66 hex chars)
        if item_prefix not in storage_items:
            storage_items[item_prefix] = []
        storage_items[item_prefix].append(key)
    
    return storage_items
```

```javascript
const getStorageKeys = async (prefix, blockHash = null) => {
  const params = blockHash ? [prefix, blockHash] : [prefix];
  
  const response = await fetch('https://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'state_getKeys',
      params: params,
      id: 1
    })
  });
  
  const data = await response.json();
  return data.result;
};

// Get all account keys (System.Account storage)
const accountPrefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9';
const accountKeys = await getStorageKeys(accountPrefix);
console.log(`Found ${accountKeys.length} accounts`);

// Extract account addresses from keys
accountKeys.forEach(key => {
  // The account address is the last 32 bytes of the key
  const addressHex = key.slice(-64);
  console.log('Account key:', key);
  console.log('Address portion:', addressHex);
});
```

```typescript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function queryStorageKeys() {
  const provider = new WsProvider('wss://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY');
  const api = await ApiPromise.create({ provider });
  
  // Method 1: Using high-level API to get keys
  const accountKeys = await api.query.system.account.keys();
  console.log('Account addresses:', accountKeys.map(k => k.toHuman()));
  
  // Method 2: Using low-level RPC for custom prefixes
  const prefix = api.query.system.account.keyPrefix();
  const keys = await api.rpc.state.getKeys(prefix);
  console.log(`Found ${keys.length} account storage keys`);
  
  // Method 3: Get keys for a specific map entry
  const validatorKeys = await api.query.staking.validators.keys();
  console.log('Active validators:', validatorKeys.length);
  
  // Process keys to extract data
  for (const key of keys) {
    // Decode the storage key
    const keyHex = key.toHex();
    console.log('Storage key:', keyHex);
    
    // Get the value for this key
    const value = await api.rpc.state.getStorage(key);
    console.log('Storage value:', value.toHex());
  }
  
  await api.disconnect();
}

// Advanced: Query keys with pagination
async function getKeysPagedExample() {
  const api = await ApiPromise.create({ 
    provider: new WsProvider('wss://api-bridge-hub-polkadot.n.dwellir.com/YOUR_API_KEY') 
  });
  
  const prefix = api.query.system.account.keyPrefix();
  const pageSize = 100;
  let startKey = prefix;
  let allKeys = [];
  
  while (true) {
    // Note: state_getKeysPaged is used for pagination
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    
    if (keys.length === 0) break;
    
    allKeys = allKeys.concat(keys);
    startKey = keys[keys.length - 1];
    
    console.log(`Fetched ${keys.length} keys, total: ${allKeys.length}`);
    
    if (keys.length < pageSize) break;
  }
  
  console.log(`Total keys found: ${allKeys.length}`);
  await api.disconnect();
}
```

## Common Storage Prefixes

| Module   | Storage Item  | Prefix (example)                                                     |
| -------- | ------------- | -------------------------------------------------------------------- |
| System   | Account       | `0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9` |
| Balances | TotalIssuance | `0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80` |
| Staking  | Validators    | `0x5f3e4907f716ac89b6347d15ececedca9320c2dc4f5d7af5b320b04e2d1a3ff3` |
| Session  | NextKeys      | `0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609` |

## Batch Query Example

```javascript
// Efficiently query multiple storage values
async function batchQueryStorage(api, keys) {
  // Get all values in a single call
  const values = await api.rpc.state.queryStorageAt(keys);
  
  const results = {};
  keys.forEach((key, index) => {
    results[key.toString()] = values[index];
  });
  
  return results;
}

// Example usage
const keys = await getStorageKeys(accountPrefix);
const values = await batchQueryStorage(api, keys.slice(0, 10));
console.log('Batch query results:', values);
```

## Use Cases

1. **Account Discovery**: Find all accounts with balances
2. **Validator Enumeration**: List all validators in the network
3. **Storage Analysis**: Analyze storage usage by module
4. **Migration Scripts**: Iterate over storage for upgrades
5. **Indexing**: Build indexes of on-chain data

## Notes

- Large prefixes may return many keys - use pagination when available
- Keys are returned in lexicographical order
- The prefix must be hex-encoded
- Consider using `state_getKeysPaged` for large datasets
- Storage keys include both the storage prefix and the key data

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bridge-hub/state_getKeysPaged) - Get keys with pagination
- [`state_getStorage`](https://www.dwellir.com/docs/bridge-hub/state_getStorage) - Get storage value
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) - Get metadata to decode keys

---

## state_getKeysPaged - Bridge Hub RPC Method

Returns storage keys matching a prefix with cursor-based pagination on Bridge Hub. This is the standard way to iterate over storage maps (like `System.Account`, `Staking.Validators`, or any pallet storage map) without loading all keys into memory at once.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_getKeysPaged` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Storage Map Iteration** -- Enumerate all entries in a storage map (accounts, balances, staking data) on Bridge Hub
- **Data Export and Indexing** -- Bulk export on-chain state for analytics, indexers, and data pipelines for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Account Enumeration** -- List all accounts that have balances, staking positions, or other on-chain state
- **State Migration Tooling** -- Iterate storage for runtime upgrades, audits, or cross-chain migration

## Best Practices

- Always use a storage key prefix to limit the result set size
- Paginate through large key sets using the `afterKey` parameter
- Combine with `state_getStorage` to retrieve values for discovered keys
- Use `state_getMetadata` to determine the correct key prefix for each pallet

## Request Parameters

- `prefix` (`String, required`): Hex-encoded storage key prefix to filter by (e.g., the pallet+storage item hash)
- `count` (`Number, required`): Maximum number of keys to return per page (recommended: 100-1000)
- `startKey` (`String, optional`): The last key from the previous page to continue from; omit for the first page
- `blockHash` (`String, optional`): Block hash for historical query; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getKeysPaged",
  "params": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
    10
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<String>, required`): Array of hex-encoded storage keys matching the prefix. Returns fewer than count entries (or empty) when the last page is reached

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da900a32c1508ad8e892b07be65125d4ba46",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901c8237c1508a37c72e20f84b137cfb8ed",
    "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getKeysPaged - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10
    ],
    "id": 1
  }'

# Continue from the last key (pagination)
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getKeysPaged",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9",
      10,
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da905c4d73a68eff3a32b6af8adc29e8fc0be"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (high-level)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get first page of System.Account keys
const prefix = api.query.system.account.keyPrefix();
const pageSize = 100;

const firstPage = await api.rpc.state.getKeysPaged(prefix, pageSize);
console.log(`First page: ${firstPage.length} keys`);

// Iterate all pages
async function getAllKeys(api, prefix, pageSize = 100) {
  const allKeys = [];
  let startKey = undefined;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;

    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
    console.log(`Fetched ${allKeys.length} keys so far...`);
  }

  return allKeys;
}

const allAccountKeys = await getAllKeys(api, prefix);
console.log(`Total accounts: ${allAccountKeys.length}`);

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getKeysPaged',
    params: [
      '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9',
      100
    ],
    id: 1
  })
});

const { result } = await response.json();
console.log(`Found ${result.length} keys`);
```

```python
import requests

def get_keys_paged(prefix, count, start_key=None, block_hash=None):
    params = [prefix, count]
    if start_key:
        params.append(start_key)
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getKeysPaged',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

def get_all_keys(prefix, page_size=100):
    """Iterate all storage keys matching a prefix."""
    all_keys = []
    start_key = None

    while True:
        keys = get_keys_paged(prefix, page_size, start_key)
        if not keys:
            break
        all_keys.extend(keys)
        start_key = keys[-1]
        print(f'Fetched {len(all_keys)} keys...')

    return all_keys

# System.Account prefix
prefix = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9'
all_keys = get_all_keys(prefix)
print(f'Total account keys: {len(all_keys)}')

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
keys = substrate.rpc_request('state_getKeysPaged', [prefix, 100])['result']
print(f'First page: {len(keys)} keys')
```

```rust
use serde_json::json;

async fn get_keys_paged(
    client: &reqwest::Client,
    url: &str,
    prefix: &str,
    count: u32,
    start_key: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let mut params: Vec<serde_json::Value> = vec![
        json!(prefix),
        json!(count),
    ];
    if let Some(key) = start_key {
        params.push(json!(key));
    }

    let response = client
        .post(url)
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getKeysPaged",
            "params": params,
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let keys: Vec<String> = result["result"]
        .as_array()
        .unwrap()
        .iter()
        .map(|v| v.as_str().unwrap().to_string())
        .collect();

    Ok(keys)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let url = "https://bridge-hub-polkadot-rpc.n.dwellir.com";
    let prefix = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9";

    // Paginate through all keys
    let mut all_keys = Vec::new();
    let mut start_key: Option<String> = None;

    loop {
        let keys = get_keys_paged(
            &client, url, prefix, 100,
            start_key.as_deref()
        ).await?;

        if keys.is_empty() { break; }
        start_key = Some(keys.last().unwrap().clone());
        all_keys.extend(keys);
        println!("Fetched {} keys...", all_keys.len());
    }

    println!("Total keys: {}", all_keys.len());
    Ok(())
}
```

## Common Use Cases

### 1. Enumerate All Accounts

List all accounts with on-chain state and fetch their balances:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function enumerateAccounts(api, pageSize = 200) {
  const prefix = api.query.system.account.keyPrefix();
  const allKeys = [];
  let startKey;

  // Paginate through all account keys
  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, pageSize, startKey);
    if (keys.length === 0) break;
    allKeys.push(...keys);
    startKey = keys[keys.length - 1];
  }

  console.log(`Found ${allKeys.length} accounts`);

  // Fetch balances in batches using queryStorageAt
  const batchSize = 100;
  for (let i = 0; i < allKeys.length; i += batchSize) {
    const batch = allKeys.slice(i, i + batchSize);
    const results = await api.rpc.state.queryStorageAt(batch);

    results[0].changes.forEach(([key, value]) => {
      if (value) {
        const accountInfo = api.createType('AccountInfo', value);
        console.log(`  Free: ${accountInfo.data.free.toHuman()}`);
      }
    });
  }
}
```

### 2. Export Storage Map for Analysis

Export all entries of a specific storage map for offline analysis:

```javascript
async function exportStorageMap(api, palletName, storageName) {
  const prefix = api.query[palletName][storageName].keyPrefix();
  const entries = [];
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 500, startKey);
    if (keys.length === 0) break;

    const values = await api.rpc.state.queryStorageAt(keys);

    for (const [key, value] of values[0].changes) {
      entries.push({
        key: key.toHex(),
        value: value ? value.toHex() : null
      });
    }

    startKey = keys[keys.length - 1];
    console.log(`Exported ${entries.length} entries...`);
  }

  return entries;
}

// Export all System.Account entries
const accounts = await exportStorageMap(api, 'system', 'account');
```

### 3. Count Storage Items by Prefix

Get a count of entries in any storage map without fetching values:

```javascript
async function countStorageKeys(api, prefix) {
  let count = 0;
  let startKey;

  while (true) {
    const keys = await api.rpc.state.getKeysPaged(prefix, 1000, startKey);
    if (keys.length === 0) break;
    count += keys.length;
    startKey = keys[keys.length - 1];
  }

  return count;
}

// Count total accounts
const accountPrefix = api.query.system.account.keyPrefix();
const totalAccounts = await countStorageKeys(api, accountPrefix);
console.log(`Total accounts on chain: ${totalAccounts}`);
```

ze or add delays between pagination requests |
\| State pruned | Historical state unavailable | Use an archive node for queries at old block hashes |
\| Timeout | Response too slow | Reduce `count` parameter (try 100 instead of 1000) |

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bridge-hub/state_getStorage) -- Get the value for a specific storage key
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bridge-hub/state_queryStorageAt) -- Batch query multiple storage keys at once
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get runtime metadata to determine storage key prefixes

---

## state_getMetadata - Bridge Hub RPC Method

Returns the runtime metadata for Bridge Hub as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_getMetadata` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Bridge Hub
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# state_getMetadata - Bridge Hub RPC Method
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Bridge Hub:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Bridge Hub:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bridge-hub/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/bridge-hub/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) - Call runtime APIs described in metadata

---

## state_getRuntimeVersion - Bridge Hub RPC Method

# state_getRuntimeVersion - Bridge Hub RPC Method

Returns the runtime version information for Bridge Hub, including the spec name, spec version, implementation version, and supported API versions.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_getRuntimeVersion` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Version Checking** - Verify runtime compatibility before constructing transactions on Bridge Hub
- **Upgrade Detection** - Monitor for runtime upgrades that may change chain behavior for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Transaction Construction** - Include the correct `specVersion` and `transactionVersion` in signed extrinsics
- **API Compatibility** - Check which runtime APIs are available and at what version

## Best Practices

- Track `specVersion` changes to detect runtime upgrades and potential forks
- The `authoringVersion` tracks block authoring protocol compatibility
- Use with `system_health` to verify node is synced before checking version
- Cache version information -- it only changes on runtime upgrades

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query version at. If omitted, returns the current runtime version

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getRuntimeVersion",
  "params": [],
  "id": 1
}
```

## Response Fields

- `specName` (`String, required`): Runtime specification name (e.g., polkadot, kusama)
- `implName` (`String, required`): Implementation name (e.g., parity-polkadot)
- `authoringVersion` (`Number, required`): Authoring version for block creation
- `specVersion` (`Number, required`): Specification version - incremented on breaking changes
- `implVersion` (`Number, required`): Implementation version - incremented on non-breaking changes
- `transactionVersion` (`Number, required`): Transaction format version - must match when signing
- `stateVersion` (`Number, required`): State trie version
- `apis` (`Array, required`): List of supported runtime API IDs and versions

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "specName": "polkadot",
    "implName": "parity-polkadot",
    "authoringVersion": 0,
    "specVersion": 1003000,
    "implVersion": 0,
    "transactionVersion": 26,
    "stateVersion": 1,
    "apis": [
      ["0xdf6acb689907609b", 5],
      ["0x37e397fc7c91f5e4", 2],
      ["0x40fe3ad401f8959a", 6]
    ]
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getRuntimeVersion",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get current runtime version
const version = await api.rpc.state.getRuntimeVersion();
console.log('Spec name:', version.specName.toString());
console.log('Spec version:', version.specVersion.toNumber());
console.log('Impl version:', version.implVersion.toNumber());
console.log('Transaction version:', version.transactionVersion.toNumber());

// Get version at a specific block
const blockHash = await api.rpc.chain.getBlockHash(1000000);
const historicalVersion = await api.rpc.state.getRuntimeVersion(blockHash);
console.log('Historical spec version:', historicalVersion.specVersion.toNumber());

await api.disconnect();
```

```python
import requests

def get_runtime_version(block_hash=None):
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getRuntimeVersion',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

version = get_runtime_version()
print(f"Spec: {version['specName']} v{version['specVersion']}")
print(f"Impl: {version['implName']} v{version['implVersion']}")
print(f"Transaction version: {version['transactionVersion']}")
print(f"Supported APIs: {len(version['apis'])}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let version = api.rpc()
        .state_get_runtime_version(None)
        .await?;

    println!("Spec name: {}", version.spec_name);
    println!("Spec version: {}", version.spec_version);
    println!("Transaction version: {}", version.transaction_version);

    Ok(())
}
```

## Common Use Cases

### 1. Runtime Upgrade Monitor

Detect runtime upgrades on Bridge Hub in real time:

```javascript
async function monitorUpgrades(api) {
  let currentVersion = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber();
  console.log(`Starting monitor at spec version: ${currentVersion}`);

  const unsub = await api.rpc.chain.subscribeNewHeads(async (header) => {
    const version = await api.rpc.state.getRuntimeVersion(header.hash);
    const newVersion = version.specVersion.toNumber();

    if (newVersion !== currentVersion) {
      console.log(`Runtime upgrade detected! ${currentVersion} -> ${newVersion}`);
      currentVersion = newVersion;
      // Trigger reconnection or metadata refresh
    }
  });

  return unsub;
}
```

### 2. Transaction Construction with Correct Version

Include the correct version fields when constructing signed extrinsics:

```javascript
async function getSigningPayloadInfo(api) {
  const version = await api.rpc.state.getRuntimeVersion();
  const genesisHash = await api.rpc.chain.getBlockHash(0);

  return {
    specVersion: version.specVersion.toNumber(),
    transactionVersion: version.transactionVersion.toNumber(),
    genesisHash: genesisHash.toHex(),
    // These fields are required for signing extrinsics
  };
}
```

### 3. Historical Version Comparison

Compare runtime versions across blocks to identify upgrade boundaries:

```javascript
async function findUpgradeBlock(api, startBlock, endBlock) {
  const startHash = await api.rpc.chain.getBlockHash(startBlock);
  const startVersion = (await api.rpc.state.getRuntimeVersion(startHash)).specVersion.toNumber();

  // Binary search for upgrade block
  let low = startBlock;
  let high = endBlock;

  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    const midHash = await api.rpc.chain.getBlockHash(mid);
    const midVersion = (await api.rpc.state.getRuntimeVersion(midHash)).specVersion.toNumber();

    if (midVersion === startVersion) {
      low = mid + 1;
    } else {
      high = mid;
    }
  }

  console.log(`Runtime upgraded at block #${low}`);
  return low;
}
```

## Related Methods

- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) - Get full runtime metadata for decoding
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) - Get node software version
- [`chain_subscribeFinalizedHeads`](https://www.dwellir.com/docs/bridge-hub/chain_subscribeFinalizedHeads) - Subscribe to detect upgrade blocks

---

## state_getStorage - Bridge Hub RPC Method

Returns the SCALE-encoded storage value for a given key on Bridge Hub. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_getStorage` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Low-Level State Access** -- Read the raw SCALE-encoded value stored under a known key on Bridge Hub
- **Metadata-Aware Tooling** -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
- **Historical State Queries** -- Read storage values at a specific block hash to analyze state changes over time
- **Pallet Storage Inspection** -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

## Best Practices

- Storage keys use pallet-specific encoding -- use `state_getMetadata` to discover key formats
- Handle `null` return values for storage keys that have never been set
- For batch storage reads, use `state_queryStorageAt` for better efficiency
- Cache storage values if querying the same key at the same block height

## Request Parameters

- `key` (`String, required`): Hex-encoded storage key (constructed from pallet name, storage item name, and optional map keys using the appropriate hashing algorithm)
- `blockHash` (`String, optional`): Block hash at which to query storage; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getStorage",
  "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
  "id": 1
}
```

## Response Fields

- `result` (`String | null, required`): Hex-encoded SCALE value at the storage key, or null if no value exists at that key

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0000000000000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error: State not available for block"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# state_getStorage - Bridge Hub RPC Method
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": ["0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"],
    "id": 1
  }'

# Query at a specific block hash
# Replace 0xYOUR_RECENT_BLOCK_HASH with a recent finalized hash from chain_getFinalizedHead
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getStorage",
    "params": [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "0xYOUR_RECENT_BLOCK_HASH"
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended -- handles key construction and decoding)
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Construct a storage key with metadata-aware helpers
const account = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const storageKey = api.query.system.account.key(account);
console.log('Storage key:', storageKey);

// Read the raw SCALE-encoded value with state_getStorage
const rawValue = await api.rpc.state.getStorage(storageKey);
console.log('Raw SCALE value:', rawValue.toHex());

// Historical read at a specific block hash
const blockHash = await api.rpc.chain.getFinalizedHead();
const historicalRaw = await api.rpc.state.getStorage(storageKey, blockHash);
console.log('Historical raw SCALE value:', historicalRaw?.toHex() ?? null);

// Metadata-aware alternative: decode the same key via api.query
const accountInfo = await api.query.system.account(account);
console.log('Decoded free balance:', accountInfo.data.free.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC) with a precomputed storage key
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_getStorage',
    params: ['0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'],
    id: 1
  })
});

const { result } = await response.json();
console.log('SCALE-encoded storage value:', result);
```

```python
import requests

def get_storage(key, block_hash=None):
    params = [key]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_getStorage',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# Query raw storage with a precomputed key
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9...'
value = get_storage(storage_key)
if value:
    print(f'Storage value: {value[:66]}...')
else:
    print('No value at this key')

# Metadata-aware alternative using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')

# High-level query with automatic SCALE decoding
result = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY']
)

print(f"Nonce: {result.value['nonce']}")
print(f"Free: {result.value['data']['free']}")
print(f"Reserved: {result.value['data']['reserved']}")

# Historical query at a specific block
result_at = substrate.query(
    module='System',
    storage_function='Account',
    params=['5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'],
    block_hash=substrate.rpc_request('chain_getFinalizedHead', [])['result']
)
print(f"Historical free: {result_at.value['data']['free']}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    // Precomputed storage key for System.Account
    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7\
        b99d880ec681799c0cf30e8886371da9\
        de1e86a9a8c739864cf3cc5ec2bea59f\
        d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;

    match result["result"].as_str() {
        Some(value) => {
            println!("SCALE-encoded value: {}", &value[..66.min(value.len())]);
            // Decode using parity-scale-codec or subxt for typed access
        }
        None => println!("No value at this storage key"),
    }

    // Query at a specific block hash
    let block_hash = "0xYOUR_RECENT_BLOCK_HASH";
    let historical = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_getStorage",
            "params": [storage_key, block_hash],
            "id": 1
        }))
        .send()
        .await?;

    let hist_result: serde_json::Value = historical.json().await?;
    println!("Historical value: {:?}", hist_result["result"]);

    Ok(())
}
```

## Common Use Cases

### 1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}
```

### 2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

```javascript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}
```

### 3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

```javascript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);
```

## Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

| Storage Type   | Key Structure                                                         | Example                                 |
| -------------- | --------------------------------------------------------------------- | --------------------------------------- |
| **Value**      | `xxhash128(Pallet) + xxhash128(Item)`                                 | `Timestamp.Now`                         |
| **Map**        | `xxhash128(Pallet) + xxhash128(Item) + hasher(Key)`                   | `System.Account(accountId)`             |
| **Double Map** | `xxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)` | `Staking.ErasStakers(era, validatorId)` |

Common hashers used in Substrate:

- **Blake2\_128Concat** -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
- **Twox64Concat** -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
- **Identity** -- Raw key with no hashing (used for already-unique keys)

## Related Methods

- [`state_getKeysPaged`](https://www.dwellir.com/docs/bridge-hub/state_getKeysPaged) -- Enumerate storage keys matching a prefix (useful for iterating map entries)
- [`state_queryStorageAt`](https://www.dwellir.com/docs/bridge-hub/state_queryStorageAt) -- Query multiple storage keys at a specific block in a single request
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get runtime metadata including storage definitions, types, and hashing algorithms
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call runtime APIs for computed state that is not directly in storage
- `state_subscribeStorage` -- Subscribe to storage changes in real time via WebSocket

---

## state_queryStorageAt - Bridge Hub RPC Method

Queries multiple storage keys at a specific block on Bridge Hub, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`state_queryStorageAt` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Consistent State Snapshots** -- Fetch multiple storage values from the same block to ensure data consistency on Bridge Hub
- **Batch Raw Storage Reads** -- Retrieve several known storage keys in one RPC call
- **Indexer and Analytics** -- Build efficient data pipelines by querying all required storage keys at once
- **Historical State Analysis** -- Compare storage state across different blocks for auditing and data analysis

## Best Practices

- Requires an archive node for querying deep historical state
- More efficient than making individual `state_getStorage` calls for multiple keys
- Accepts multiple storage keys in a single request for batch retrieval
- Use block hashes (not numbers) for deterministic historical queries

## Request Parameters

- `keys` (`Array<String>, required`): Array of hex-encoded storage keys to query
- `blockHash` (`String, optional`): Block hash to query at; defaults to the best block if omitted

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_queryStorageAt",
  "params": [
    [
      "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
    ]
  ],
  "id": 1
}
```

## Response Fields

- `block` (`String, required`): The block hash at which the query was executed
- `changes` (`Array<[String, String|null]>, required`): Array of [key, value] pairs. The value is a hex-encoded SCALE value, or null if the key does not exist

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "block": "0x1a2b3c4d5e6f...",
      "changes": [
        [
          "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
          "0x0100000000000000010000000000000000407a10f35a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        ]
      ]
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_queryStorageAt",
    "params": [
      [
        "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
      ]
    ],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api helpers to construct storage keys
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// High-level: query multiple accounts at once
const accounts = [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'
];
const storageKeys = await Promise.all(
  accounts.map((addr) => api.query.system.account.key(addr))
);

const queryResult = await api.rpc.state.queryStorageAt(storageKeys);
console.log('Block:', queryResult[0].block.toHex());
console.log('Changes:', queryResult[0].changes.length);

// Metadata-aware alternative: decode those same accounts at the latest state
const decoded = await api.query.system.account.multi(accounts);
decoded.forEach((info, idx) => {
  console.log(`Decoded account ${accounts[idx]} free balance:`, info.data.free.toString());
});

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'state_queryStorageAt',
    params: [storageKeys.map((k) => k.toHex())],
    id: 1
  })
});

const { result } = await response.json();
console.log('Queried at block:', result[0].block);
```

```python
import requests

def query_storage_at(keys, block_hash=None):
    params = [keys]
    if block_hash:
        params.append(block_hash)

    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'state_queryStorageAt',
            'params': params,
            'id': 1
        }
    )
    return response.json()['result']

# state_queryStorageAt - Bridge Hub RPC Method
storage_key = '0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'
result = query_storage_at([storage_key])
print(f"Block: {result[0]['block']}")
for key, value in result[0]['changes']:
    print(f"  Key: {key[:40]}...")
    print(f"  Value: {value[:40] if value else 'null'}...")

# Using substrate-interface
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
result = substrate.rpc_request('state_queryStorageAt', [[storage_key]])['result']
print(f"Changes: {len(result[0]['changes'])}")
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();

    let storage_key = "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d";

    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "state_queryStorageAt",
            "params": [[storage_key]],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let entries = &result["result"][0];

    println!("Block: {}", entries["block"]);
    if let Some(changes) = entries["changes"].as_array() {
        for change in changes {
            let key = change[0].as_str().unwrap_or("");
            let value = change[1].as_str().unwrap_or("null");
            println!("  Key: {}...", &key[..std::cmp::min(40, key.len())]);
            println!("  Value: {}...", &value[..std::cmp::min(40, value.len())]);
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Multi-Key Snapshot

Read multiple storage keys from the same block:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});
```

### 2. Historical State Comparison

Compare storage state between two blocks for auditing:

```javascript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}
```

### 3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

```javascript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}
```

## Related Methods

- [`state_getStorage`](https://www.dwellir.com/docs/bridge-hub/state_getStorage) -- Query a single storage key
- [`state_getKeysPaged`](https://www.dwellir.com/docs/bridge-hub/state_getKeysPaged) -- Enumerate storage keys with pagination
- [`state_call`](https://www.dwellir.com/docs/bridge-hub/state_call) -- Call a runtime API function
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get runtime metadata to construct storage keys
- [`chain_getBlockHash`](https://www.dwellir.com/docs/bridge-hub/chain_getBlockHash) -- Get a block hash by block number for historical queries

---

## system_chain - Bridge Hub RPC Method

Returns the chain name of the Bridge Hub network. This identifies the specific chain or network the node is connected to (e.g., `"Polkadot"`, `"Kusama"`, `"Westend"`).

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_chain` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Network Verification** -- Confirm your application is connected to the correct Bridge Hub network before processing transactions
- **Multi-Chain Applications** -- Dynamically identify which Substrate chain you are interacting with in cross-chain or multi-network dApps
- **UI Display** -- Show the connected network name in wallet interfaces and dashboards for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Configuration Validation** -- Verify endpoint configuration matches the expected chain during deployment

## Best Practices

- Cache the chain name at startup -- it does not change during a session
- Use with `system_properties` for complete chain identification (name, token, decimals)
- Chain name is a simple string identifier, not a unique numeric ID
- For multi-chain applications, maintain a mapping of chain names to app configuration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_chain",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The human-readable chain name (e.g., "Polkadot", "Kusama", "Acala")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Bridge Hub"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_chain",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const chain = await api.rpc.system.chain();
console.log('Connected to chain:', chain.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_chain',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Connected to chain:', result);
```

```python
import requests

def get_chain_name():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_chain',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

chain = get_chain_name()
print(f'Connected to chain: {chain}')

# system_chain - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
chain = substrate.rpc_request('system_chain', [])['result']
print(f'Connected to chain: {chain}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_chain",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Connected to chain: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Network Connection Verification

Validate that your application connects to the correct chain before processing any transactions:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function connectAndVerify(endpoint, expectedChain) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const chain = await api.rpc.system.chain();
  const chainName = chain.toString();

  if (chainName !== expectedChain) {
    await api.disconnect();
    throw new Error(
      `Expected "${expectedChain}" but connected to "${chainName}"`
    );
  }

  console.log(`Verified connection to ${chainName}`);
  return api;
}

// Usage
const api = await connectAndVerify('https://bridge-hub-polkadot-rpc.n.dwellir.com', 'Bridge Hub');
```

### 2. Multi-Chain Router

Route operations based on detected chain identity:

```javascript
async function getChainConfig(api) {
  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const chainName = chain.toString();
  const configs = {
    Polkadot: { explorer: 'https://polkadot.subscan.io', confirmations: 1 },
    Kusama: { explorer: 'https://kusama.subscan.io', confirmations: 1 },
  };

  const config = configs[chainName] || { explorer: null, confirmations: 1 };

  return {
    name: chainName,
    tokenSymbol: properties.tokenSymbol.toString(),
    tokenDecimals: properties.tokenDecimals.toJSON(),
    ...config
  };
}
```

### 3. Health Check with Chain Identity

Include chain identity in health-check monitoring:

```javascript
async function healthCheck(api) {
  const [chain, name, version] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.name(),
    api.rpc.system.version()
  ]);

  return {
    status: 'healthy',
    chain: chain.toString(),
    nodeImplementation: name.toString(),
    nodeVersion: version.toString(),
    timestamp: new Date().toISOString()
  };
}
```

## Related Methods

- [`system_name`](https://www.dwellir.com/docs/bridge-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node implementation version
- [`system_properties`](https://www.dwellir.com/docs/bridge-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bridge-hub/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bridge-hub/rpc_methods) -- List all available RPC methods

---

## system_health - Bridge Hub RPC Method

# system_health - Bridge Hub RPC Method

Returns the health status of the Bridge Hub node, including peer count, sync state, and whether the node expects to have peers.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_health` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Health Checks** - Monitor node availability and readiness before routing traffic on Bridge Hub
- **Load Balancing** - Route requests only to healthy, fully synced nodes for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Sync Status** - Verify a node is caught up before trusting its state queries
- **Infrastructure Alerts** - Trigger alerts when peers drop or sync stalls

## Best Practices

- Call at application startup before processing any transactions
- If `isSyncing` is `true`, delay all transaction operations until it returns `false`
- Low `peers` count may indicate network connectivity issues
- Combine with `system_chain` and `system_version` for a complete node health check

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_health",
  "params": [],
  "id": 1
}
```

## Response Fields

- `peers` (`Number, required`): Number of connected peers
- `isSyncing` (`Boolean, required`): true if the node is still syncing with the network
- `shouldHavePeers` (`Boolean, required`): true if the node is expected to have peers (false for local dev chains)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": 42,
    "isSyncing": false,
    "shouldHavePeers": true
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_health",
    "params": [],
    "id": 1
  }'
```

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const health = await api.rpc.system.health();
console.log('Peers:', health.peers.toNumber());
console.log('Is syncing:', health.isSyncing.isTrue);
console.log('Should have peers:', health.shouldHavePeers.isTrue);

const isHealthy = !health.isSyncing.isTrue && health.peers.toNumber() > 0;
console.log('Node healthy:', isHealthy);

await api.disconnect();
```

```python
import requests

def get_health():
    url = 'https://bridge-hub-polkadot-rpc.n.dwellir.com'

    payload = {
        'jsonrpc': '2.0',
        'method': 'system_health',
        'params': [],
        'id': 1
    }

    response = requests.post(url, json=payload)
    result = response.json()

    if 'error' in result:
        raise Exception(f"RPC Error: {result['error']}")

    return result['result']

health = get_health()
print(f"Peers: {health['peers']}")
print(f"Syncing: {health['isSyncing']}")
print(f"Should have peers: {health['shouldHavePeers']}")

is_healthy = not health['isSyncing'] and health['peers'] > 0
print(f"Node healthy: {is_healthy}")
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://bridge-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let health = api.rpc()
        .system_health()
        .await?;

    println!("Peers: {}", health.peers);
    println!("Is syncing: {}", health.is_syncing);
    println!("Should have peers: {}", health.should_have_peers);

    let is_healthy = !health.is_syncing && health.peers > 0;
    println!("Node healthy: {}", is_healthy);

    Ok(())
}
```

## Common Use Cases

### 1. Readiness Probe for Kubernetes

Use as a health check endpoint for container orchestration on Bridge Hub:

```javascript
import express from 'express';
import { ApiPromise, WsProvider } from '@polkadot/api';

const app = express();
const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

app.get('/healthz', async (req, res) => {
  try {
    const health = await api.rpc.system.health();
    const isReady = !health.isSyncing.isTrue && health.peers.toNumber() > 0;

    if (isReady) {
      res.status(200).json({ status: 'healthy', peers: health.peers.toNumber() });
    } else {
      res.status(503).json({
        status: 'not ready',
        syncing: health.isSyncing.isTrue,
        peers: health.peers.toNumber()
      });
    }
  } catch (error) {
    res.status(503).json({ status: 'unreachable', error: error.message });
  }
});
```

### 2. Multi-Node Load Balancer

Route traffic only to healthy Bridge Hub nodes:

```javascript
async function selectHealthyNode(endpoints) {
  const results = await Promise.allSettled(
    endpoints.map(async (endpoint) => {
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method: 'system_health',
          params: [],
          id: 1
        })
      });

      const { result } = await response.json();
      return { endpoint, ...result };
    })
  );

  const healthy = results
    .filter(r => r.status === 'fulfilled' && !r.value.isSyncing)
    .map(r => r.value)
    .sort((a, b) => b.peers - a.peers);

  return healthy.length > 0 ? healthy[0].endpoint : null;
}
```

### 3. Continuous Health Monitor

Periodically check node health and alert on degradation:

```python
import requests
import time

def monitor_health(endpoint, interval=30, min_peers=5):
    while True:
        try:
            payload = {
                'jsonrpc': '2.0',
                'method': 'system_health',
                'params': [],
                'id': 1
            }

            response = requests.post(endpoint, json=payload, timeout=5)
            health = response.json()['result']

            peers = health['peers']
            syncing = health['isSyncing']

            if syncing:
                print(f'WARNING: Node is syncing (peers: {peers})')
            elif peers < min_peers:
                print(f'WARNING: Low peer count: {peers}')
            else:
                print(f'OK: peers={peers}, syncing={syncing}')

        except Exception as e:
            print(f'ERROR: Node unreachable - {e}')

        time.sleep(interval)

monitor_health('https://bridge-hub-polkadot-rpc.n.dwellir.com')
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) - Get node software version
- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) - Get chain name
- `system_syncState` - Get detailed sync progress
- `system_peers` - Get detailed peer information

---

## system_name - Bridge Hub RPC Method

Returns the node implementation name on Bridge Hub. This identifies the client software running the node (e.g., `"Parity Polkadot"`, `"Substrate Node"`, `"Astar Collator"`).

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_name` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Client Identification** -- Determine which Substrate client implementation your node is running (useful when multiple implementations exist)
- **Infrastructure Monitoring** -- Track client types across your validator or collator fleet on Bridge Hub
- **Bug Reports and Diagnostics** -- Include client implementation details when reporting issues for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Compatibility Checks** -- Verify that the node implementation supports features required by your application

## Best Practices

- Provides client implementation info -- equivalent to `web3_clientVersion` on EVM chains
- Include this output in bug reports when troubleshooting node behavior
- Different client implementations (Substrate, Polkadot SDK, Cumulus) return different names
- Use with `system_version` for the complete software identity

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_name",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation name (e.g., "Parity Polkadot", "Substrate Node")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "Parity Polkadot"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_name",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const name = await api.rpc.system.name();
console.log('Bridge Hub node implementation:', name.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_name',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bridge Hub node implementation:', result);
```

```python
import requests

def get_node_name():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_name',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

name = get_node_name()
print(f'Bridge Hub node implementation: {name}')

# system_name - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
name = substrate.rpc_request('system_name', [])['result']
print(f'Bridge Hub node implementation: {name}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_name",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bridge Hub node implementation: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Full Node Identity Report

Gather complete node identity details in a single call:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function getNodeIdentity(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [name, version, chain] = await Promise.all([
    api.rpc.system.name(),
    api.rpc.system.version(),
    api.rpc.system.chain()
  ]);

  const identity = {
    implementation: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    endpoint
  };

  await api.disconnect();
  return identity;
}

// Example output:
// { implementation: "Parity Polkadot", version: "0.9.43-ba6af17", chain: "Polkadot", endpoint: "..." }
```

### 2. Infrastructure Audit Across Nodes

Audit client implementations across a fleet of Bridge Hub nodes:

```javascript
async function auditFleetClients(endpoints) {
  const results = await Promise.all(
    endpoints.map(async (endpoint) => {
      try {
        const provider = new WsProvider(endpoint);
        const api = await ApiPromise.create({ provider });
        const name = await api.rpc.system.name();
        const version = await api.rpc.system.version();
        await api.disconnect();
        return { endpoint, client: name.toString(), version: version.toString(), status: 'ok' };
      } catch (error) {
        return { endpoint, client: null, version: null, status: 'unreachable' };
      }
    })
  );

  // Group by client implementation
  const byClient = {};
  for (const node of results) {
    if (node.client) {
      byClient[node.client] = byClient[node.client] || [];
      byClient[node.client].push(node);
    }
  }

  console.log('Client distribution:', Object.keys(byClient).map(
    (k) => `${k}: ${byClient[k].length} nodes`
  ));

  return results;
}
```

### 3. Connection Health Check with Client Info

Include client implementation in health-check responses:

```javascript
async function healthCheckWithClientInfo(api) {
  try {
    const name = await api.rpc.system.name();
    const version = await api.rpc.system.version();
    const chain = await api.rpc.system.chain();

    return {
      healthy: true,
      client: `${name.toString()} v${version.toString()}`,
      chain: chain.toString(),
      checkedAt: new Date().toISOString()
    };
  } catch (error) {
    return {
      healthy: false,
      error: error.message,
      checkedAt: new Date().toISOString()
    };
  }
}
```

## Related Methods

- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node implementation version
- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`system_properties`](https://www.dwellir.com/docs/bridge-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bridge-hub/state_getRuntimeVersion) -- Get the on-chain runtime version
- [`rpc_methods`](https://www.dwellir.com/docs/bridge-hub/rpc_methods) -- List all available RPC methods

---

## system_properties - Bridge Hub RPC Method

Returns the chain-specific properties for Bridge Hub, including the native token symbol, token decimals, and the address-format prefix when the chain exposes one. This information is critical for correctly formatting balances, validating addresses, and configuring wallets.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_properties` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Token Formatting** -- Get the correct decimals and symbol to display human-readable balances on Bridge Hub
- **Address Validation** -- Retrieve the SS58 prefix to encode and validate addresses for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Wallet and dApp Configuration** -- Dynamically configure your UI with the correct token symbol, decimals, and address format
- **Multi-Chain Support** -- Automatically adapt your application to different Substrate chains without hardcoding properties

## Best Practices

- `tokenDecimals` determines on-chain amount display (verified: Polkadot returns 10 decimals for DOT)
- `tokenSymbol` provides the native token ticker for UI display
- `ss58Format` is the address encoding prefix for this chain (0 for Polkadot, 2 for Kusama)
- Cache these properties at startup -- they do not change without a chain migration

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_properties",
  "params": [],
  "id": 1
}
```

## Response Fields

- `ss58Format or SS58Prefix` (`Number, required`): The SS58 address format prefix used by this chain, when the chain exposes one
- `tokenDecimals` (`Number | Array<Number>, required`): Number of decimal places for the native token, or an array for multi-token chains
- `tokenSymbol` (`String | Array<String>, required`): Native token symbol, or an array for multi-token chains

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "ss58Format": 42,
    "tokenDecimals": 9,
    "tokenSymbol": "TOKEN"
  }
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_properties",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const properties = await api.rpc.system.properties();

const raw = properties.toJSON();
const tokenSymbol = Array.isArray(raw.tokenSymbol) ? raw.tokenSymbol : [raw.tokenSymbol];
const tokenDecimals = Array.isArray(raw.tokenDecimals) ? raw.tokenDecimals : [raw.tokenDecimals];
const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

console.log('Token symbol:', tokenSymbol);
console.log('Token decimals:', tokenDecimals);
console.log('SS58 format:', ss58Format ?? 'not exposed');

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_properties',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Properties:', result);
```

```python
import requests

def get_chain_properties():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_properties',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

props = get_chain_properties()
token_symbol = props['tokenSymbol']
token_decimals = props['tokenDecimals']
ss58_format = props.get('ss58Format', props.get('SS58Prefix'))

print(f"Token: {token_symbol}")
print(f"Decimals: {token_decimals}")
print(f"SS58 Format: {ss58_format if ss58_format is not None else 'not exposed'}")

# system_properties - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
props = substrate.properties
print(f"Token: {props.get('tokenSymbol')}")
print(f"Decimals: {props.get('tokenDecimals')}")
print(f"SS58 Format: {props.get('ss58Format', props.get('SS58Prefix', 'not exposed'))}")
```

```rust
use serde_json::json;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ChainProperties {
    #[serde(alias = "SS58Prefix")]
    ss58_format: Option<u16>,
    token_decimals: Option<serde_json::Value>,
    token_symbol: Option<serde_json::Value>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_properties",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    let props: ChainProperties = serde_json::from_value(result["result"].clone())?;

    println!("SS58 Format: {:?}", props.ss58_format);
    println!("Token Decimals: {:?}", props.token_decimals);
    println!("Token Symbol: {:?}", props.token_symbol);
    Ok(())
}
```

## Common Use Cases

### 1. Human-Readable Balance Formatting

Format raw on-chain balances into human-readable token amounts:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';
import { BN } from '@polkadot/util';

async function formatBalance(api, rawBalance) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const decimalsRaw = raw.tokenDecimals;
  const symbolRaw = raw.tokenSymbol;
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw[0] : decimalsRaw;
  const symbol = Array.isArray(symbolRaw) ? symbolRaw[0] : symbolRaw;

  const divisor = new BN(10).pow(new BN(decimals));
  const whole = new BN(rawBalance).div(divisor);
  const fractional = new BN(rawBalance).mod(divisor).toString().padStart(decimals, '0');

  return `${whole}.${fractional.slice(0, 4)} ${symbol}`;
}

// Example output depends on the chain's live token symbol and decimals.
```

### 2. Dynamic Wallet Configuration

Auto-configure your wallet or dApp based on chain properties:

```javascript
async function configureWallet(endpoint) {
  const provider = new WsProvider(endpoint);
  const api = await ApiPromise.create({ provider });

  const [chain, properties] = await Promise.all([
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  const raw = properties.toJSON();
  const symbolsRaw = raw.tokenSymbol;
  const decimalsRaw = raw.tokenDecimals;
  const symbols = Array.isArray(symbolsRaw) ? symbolsRaw : [symbolsRaw];
  const decimals = Array.isArray(decimalsRaw) ? decimalsRaw : [decimalsRaw];
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix ?? null;

  const config = {
    chainName: chain.toString(),
    ss58Format,
    tokens: symbols.map((symbol, idx) => ({
      symbol,
      decimals: decimals[idx] ?? decimals[0],
    }))
  };

  console.log('Wallet configured for:', config.chainName);
  console.log('Native token:', config.tokens[0].symbol, `(${config.tokens[0].decimals} decimals)`);
  console.log('Address format SS58:', config.ss58Format ?? 'not exposed');

  await api.disconnect();
  return config;
}
```

### 3. SS58 Address Encoding and Validation

Use the SS58 prefix to properly encode addresses for the target chain:

```javascript
import { encodeAddress, decodeAddress } from '@polkadot/util-crypto';

async function formatAddressForChain(api, genericAddress) {
  const properties = await api.rpc.system.properties();
  const raw = properties.toJSON();
  const ss58Format = raw.ss58Format ?? raw.SS58Prefix;

  if (ss58Format == null) {
    throw new Error('This chain does not expose an SS58 prefix through system_properties.');
  }

  // Convert any SS58 address to this chain's format
  const publicKey = decodeAddress(genericAddress);
  const chainAddress = encodeAddress(publicKey, ss58Format);

  console.log(`Address on ${ss58Format}: ${chainAddress}`);
  return chainAddress;
}
```

ze scalar vs array values and fall back to `SS58Prefix` when `ss58Format` is absent |

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bridge-hub/system_name) -- Get the node implementation name
- [`system_version`](https://www.dwellir.com/docs/bridge-hub/system_version) -- Get the node implementation version
- [`state_getMetadata`](https://www.dwellir.com/docs/bridge-hub/state_getMetadata) -- Get full runtime metadata including pallet definitions
- [`rpc_methods`](https://www.dwellir.com/docs/bridge-hub/rpc_methods) -- List all available RPC methods

---

## system_version - Bridge Hub RPC Method

Returns the node implementation version string on Bridge Hub. This version reflects the client software version (e.g., `0.9.43-ba6af1743a0`), not the on-chain runtime version.

> **Why Bridge Hub?** Build on Polkadot's trustless bridging parachain with $75M+ TVL via Snowbridge to Ethereum with on-chain BEEFY/Beacon light clients (no multisigs), 100+ ERC-20 tokens supported, 24+ parachain integrations, and 1-2 minute transfer times.

## When to Use This Method

`system_version` is essential for cross-chain developers, bridge operators, and teams requiring trustless Ethereum-Polkadot transfers:

- **Compatibility Checking** -- Verify the node client version supports the features your application requires on Bridge Hub
- **Upgrade Monitoring** -- Track node software versions across your validator or collator fleet after runtime upgrades
- **Diagnostics and Debugging** -- Include version information in bug reports and support requests for trustless ETH and ERC-20 bridging, cross-chain messaging, and parachain-to-Ethereum asset transfers
- **Multi-Node Management** -- Ensure all nodes in your infrastructure are running consistent versions

## Best Practices

- Check the runtime version before using version-specific Substrate APIs
- Track version changes during runtime upgrades to detect compatibility issues
- Use with `system_chain` and `system_properties` for full network context
- Different nodes on the same network should return the same version (unless upgrading)

## Request Parameters

This method accepts no parameters.

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "system_version",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`String, required`): The node implementation version string (e.g., "0.9.43-ba6af1743a0")

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0.9.43-ba6af1743a0"
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://bridge-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "system_version",
    "params": [],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';

const provider = new WsProvider('wss://bridge-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

const version = await api.rpc.system.version();
console.log('Bridge Hub node version:', version.toString());

await api.disconnect();

// Using fetch (HTTP JSON-RPC)
const response = await fetch('https://bridge-hub-polkadot-rpc.n.dwellir.com', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'system_version',
    params: [],
    id: 1
  })
});

const { result } = await response.json();
console.log('Bridge Hub node version:', result);
```

```python
import requests

def get_system_version():
    response = requests.post(
        'https://bridge-hub-polkadot-rpc.n.dwellir.com',
        json={
            'jsonrpc': '2.0',
            'method': 'system_version',
            'params': [],
            'id': 1
        }
    )
    return response.json()['result']

version = get_system_version()
print(f'Bridge Hub node version: {version}')

# system_version - Bridge Hub RPC Method
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url='wss://bridge-hub-polkadot-rpc.n.dwellir.com')
version = substrate.rpc_request('system_version', [])['result']
print(f'Bridge Hub node version: {version}')
```

```rust
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let response = client
        .post("https://bridge-hub-polkadot-rpc.n.dwellir.com")
        .json(&json!({
            "jsonrpc": "2.0",
            "method": "system_version",
            "params": [],
            "id": 1
        }))
        .send()
        .await?;

    let result: serde_json::Value = response.json().await?;
    println!("Bridge Hub node version: {}", result["result"]);
    Ok(())
}
```

## Common Use Cases

### 1. Node Fleet Version Monitoring

Track version consistency across multiple Bridge Hub nodes:

```javascript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function checkFleetVersions(endpoints) {
  const versions = await Promise.all(
    endpoints.map(async (endpoint) => {
      const provider = new WsProvider(endpoint);
      const api = await ApiPromise.create({ provider });
      const version = await api.rpc.system.version();
      const name = await api.rpc.system.name();
      await api.disconnect();
      return { endpoint, version: version.toString(), name: name.toString() };
    })
  );

  const unique = new Set(versions.map((v) => v.version));
  if (unique.size > 1) {
    console.warn('Version mismatch detected across fleet!');
  }

  versions.forEach((v) => {
    console.log(`${v.endpoint}: ${v.name} v${v.version}`);
  });
}
```

### 2. Pre-Upgrade Compatibility Check

Verify node version before executing operations:

```javascript
async function ensureMinVersion(api, minVersion) {
  const version = await api.rpc.system.version();
  const versionStr = version.toString();
  const [major, minor, patch] = versionStr.split('-')[0].split('.').map(Number);
  const [minMajor, minMinor, minPatch] = minVersion.split('.').map(Number);

  if (
    major < minMajor ||
    (major === minMajor && minor < minMinor) ||
    (major === minMajor && minor === minMinor && patch < minPatch)
  ) {
    throw new Error(
      `Node version ${versionStr} is below minimum ${minVersion}`
    );
  }

  console.log(`Node version ${versionStr} meets minimum ${minVersion}`);
  return true;
}
```

### 3. Node Identity Dashboard

Gather full node identity information:

```javascript
async function getNodeIdentity(api) {
  const [version, name, chain, properties] = await Promise.all([
    api.rpc.system.version(),
    api.rpc.system.name(),
    api.rpc.system.chain(),
    api.rpc.system.properties()
  ]);

  return {
    client: name.toString(),
    version: version.toString(),
    chain: chain.toString(),
    tokenSymbol: properties.tokenSymbol.toString(),
    ss58Format: properties.ss58Format.toString()
  };
}
```

## Related Methods

- [`system_chain`](https://www.dwellir.com/docs/bridge-hub/system_chain) -- Get the chain name
- [`system_name`](https://www.dwellir.com/docs/bridge-hub/system_name) -- Get the node implementation name
- [`system_properties`](https://www.dwellir.com/docs/bridge-hub/system_properties) -- Get chain properties (token symbol, decimals, SS58 format)
- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/bridge-hub/state_getRuntimeVersion) -- Get the on-chain runtime version (spec version, impl version)
- [`rpc_methods`](https://www.dwellir.com/docs/bridge-hub/rpc_methods) -- List all available RPC methods

---

## Binance Smart Chain - BNB Chain Documentation

# Binance Smart Chain - BNB Chain Documentation

## Why Build on Binance Smart Chain?

Binance Smart Chain (BSC) is a fast, low-cost blockchain that runs parallel to Binance Chain, offering EVM compatibility and a thriving ecosystem:

### **High Performance**

- **3-second block times** - Fast transaction confirmations
- **Low transaction costs** - Significantly cheaper than Ethereum
- **High throughput** - Handles high transaction volumes efficiently

### **EVM Compatibility**

- **Binance ecosystem** - Backed by world's largest crypto exchange
- **Ethereum compatibility** - Deploy Ethereum dApps without changes
- **Mature infrastructure** - Battle-tested with billions in TVL

### **Vibrant Ecosystem**

- **PancakeSwap** - Leading DEX with massive liquidity
- **BNB token integration** - Native support for BNB ecosystem
- **Cross-chain bridges** - Easy asset transfers from other chains

## Quick Start with Binance Smart Chain

Connect to Binance Smart Chain in seconds with Dwellir's optimized endpoints:

### Installation & Setup

Ethers.js v6
Web3.js
Viem

```javascript
import { JsonRpcProvider } from 'ethers';

// Connect to BSC mainnet
const provider = new JsonRpcProvider(
  'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'
);

// Get the latest block
const block = await provider.getBlock('latest');
console.log('Latest block:', block.number);

// Query account balance
const balance = await provider.getBalance('0x...');
console.log('Balance:', balance.toString());
```

```javascript
const Web3 = require('web3');

// Connect to BSC mainnet
const web3 = new Web3(
  'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'
);

// Get chain ID to verify connection
const chainId = await web3.eth.getChainId();
console.log('Connected to BSC:', chainId === 56);

// Get gas price for optimal transaction pricing
const gasPrice = await web3.eth.getGasPrice();
console.log('Current gas price:', gasPrice);
```

```typescript
import { createPublicClient, http } from 'viem';
import { bsc } from 'viem/chains';

// Create BSC client
const client = createPublicClient({
  chain: bsc,
  transport: http('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'),
});

// Read contract data
const data = await client.readContract({
  address: '0x...',
  abi: contractAbi,
  functionName: 'balanceOf',
  args: ['0x...'],
});
```

## Network Information

| Parameter    | Value     | Details      |
| ------------ | --------- | ------------ |
| Chain ID     | 56        | Mainnet      |
| Block Time   | 3 seconds | Average      |
| Gas Token    | BNB       | Native token |
| RPC Standard | Ethereum  | JSON-RPC 2.0 |

## API Reference

Binance Smart Chain supports the full [Ethereum JSON-RPC API specification](https://ethereum.org/developers/docs/apis/json-rpc/) with EVM compatibility. Access all standard Ethereum methods on the BNB ecosystem.

## Common Integration Patterns

### Transaction Monitoring

Monitor pending and confirmed transactions efficiently on BSC:

```javascript
// Watch for transaction confirmation
async function waitForTransaction(txHash) {
  const receipt = await provider.waitForTransaction(txHash, 1);

  // BSC has fast 3-second block times
  console.log('Transaction confirmed in block:', receipt.blockNumber);
  console.log('Gas used:', receipt.gasUsed.toString());

  return receipt;
}
```

### Gas Optimization

Optimize gas costs on Binance Smart Chain:

```javascript
// Estimate gas for BSC transaction
const gasEstimate = await provider.estimateGas(tx);

// Get current gas price (BSC uses standard gas pricing)
const gasPrice = await provider.getGasPrice();

// Calculate total cost in BNB
const totalCost = gasEstimate * gasPrice;
console.log('Total cost in BNB:', formatEther(totalCost));
```

### Event Filtering

Efficiently query contract events on BSC:

```javascript
// Query events with automatic retry and pagination
async function getEvents(contract, eventName, fromBlock = 0) {
  const filter = contract.filters[eventName]();
  const events = [];
  const batchSize = 5000; // BSC can handle larger batch sizes

  for (let i = fromBlock; i <= currentBlock; i += batchSize) {
    const batch = await contract.queryFilter(
      filter,
      i,
      Math.min(i + batchSize - 1, currentBlock)
    );
    events.push(...batch);

  return events;
}
```

## Performance Best Practices

### 1. **Batch Requests**

Combine multiple RPC calls for optimal performance:

```javascript
const batch = [
  { method: 'eth_blockNumber', params: [] },
  { method: 'eth_gasPrice', params: [] },
  { method: 'eth_getBalance', params: [address, 'latest'] }
];

const results = await provider.send(batch);
```

### 2. **Connection Pooling**

Reuse provider instances to minimize connection overhead:

```javascript
// Singleton pattern for provider
class BSCProvider {
  static instance = null;

  static getInstance() {
    if (!this.instance) {
      this.instance = new JsonRpcProvider(
        'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'
      );
    }
    return this.instance;
  }
}
```

### 3. **Smart Caching**

Cache immutable data to reduce API calls:

```javascript
const cache = new Map();

async function getCachedBlockData(blockNumber) {
  const key = `block_${blockNumber}`;

  if (!cache.has(key)) {
    const block = await provider.getBlock(blockNumber);
    cache.set(key, block);
  }

  return cache.get(key);
}
```

## Troubleshooting Common Issues

### Error: "Insufficient funds"

BSC transactions require BNB for gas fees:

```javascript
// Check BNB balance for gas fees
const balance = await provider.getBalance(address);
const gasEstimate = await provider.estimateGas(tx);
const gasPrice = await provider.getGasPrice();
const totalRequired = gasEstimate * gasPrice + tx.value;

if (balance < totalRequired) {
  const needed = formatEther(totalRequired - balance);
  throw new Error(`Need ${needed} more BNB`);
}
```

### Error: "Transaction underpriced"

BSC uses legacy gas pricing. Use current gas price:

```javascript
// Get current gas price
const gasPrice = await provider.getGasPrice();

const tx = {
  to: recipient,
  value: amount,
  gasPrice: gasPrice,
  gasLimit: 21000n,
  type: 0 // Legacy transaction type
};
```

### Error: "Rate limit exceeded"

Implement exponential backoff for resilient applications:

```javascript
async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 2 ** i * 1000));
      } else {
        throw error;
      }
    }
  }
}
```

## Migration Guide

### From Ethereum Mainnet

Moving from Ethereum to Binance Smart Chain requires minimal changes:

```javascript
// Before (Ethereum)
const provider = new JsonRpcProvider('https://eth-rpc.example.com');

// After (BSC)
const provider = new JsonRpcProvider(
  'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'
);

// Smart contracts work identically
// Same tooling and libraries
// Note: Different chain ID (56)
// Note: Separate block numbers
// Note: Different gas token (BNB instead of ETH)
// Note: Legacy gas pricing (no EIP-1559)
```

## Resources & Tools

### Official Resources

- [BSC Documentation](https://docs.bnbchain.org/)
- [BSC Bridge](https://www.bnbchain.org/en/bridge)
- [BscScan Block Explorer](https://bscscan.com/)

### Developer Tools

- [Developer Tools](https://docs.bnbchain.org/)
- [Remix IDE](https://remix.ethereum.org/)

### Need Help?

- **Email**: <support@dwellir.com>
- **Docs**: You're here!
- **Dashboard**: [dashboard.dwellir.com](https://dashboard.dwellir.com)

***

*Start building on Binance Smart Chain with Dwellir's enterprise-grade RPC infrastructure. [Get your API key](https://dashboard.dwellir.com/register)*

---

## debug_traceBlock - BSC RPC Method

Traces all transactions in a block on Binance Smart Chain by accepting a serialized block payload. Returns detailed execution traces for every transaction in the block, including opcode-level steps, gas consumption, and internal calls.

> **Why BSC?** Build on the third-largest blockchain by market cap with $12B+ TVL and 37%+ DEX market share with sub-$0.10 fees, 2.6M daily active users, full EVM compatibility, and direct Binance integration.

BSC API endpoints are full nodes with debug APIs enabled. Debug methods work for blocks and transactions whose state is still retained on the node. Older historical state requires an archive node, available as a dedicated node or dedicated cluster.

## When to Use This Method

`debug_traceBlock` is valuable for DeFi developers, trading platform builders, and teams seeking Binance ecosystem access:

- **Block-Level Debugging** - Trace every transaction in a block simultaneously when you have the serialized block payload, useful for offline analysis or replaying captured block data
- **Gas Profiling Across Transactions** - Measure gas consumption per opcode across all transactions in a block to identify expensive patterns on BSC
- **MEV Analysis** - Analyze transaction ordering, sandwich attacks, and arbitrage patterns by tracing full block execution for high-frequency DeFi (PancakeSwap), NFT marketplaces, and GameFi applications
- **Protocol Research** - Replay historical blocks from RLP data to study state transitions and EVM behavior

## Best Practices

- Requires archive node access; not available on standard full nodes
- Block traces can be very resource-intensive on densely packed blocks
- Consider tracing individual transactions instead for targeted analysis
- Prefer debug\_traceBlockByNumber or debug\_traceBlockByHash for simpler workflows

## Request Parameters

- `blockPayload` (`DATA, required`): Serialized block payload as a hex string
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlock",
  "params": [
    "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`QUANTITY, required`): Gas cost of this opcode
- `structLogs[].depth` (`QUANTITY, required`): Call depth
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `calls` (`Array, required`): Sub-calls made during execution

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "DELEGATECALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x9c40",
            "input": "0xa9059cbb...",
            "output": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
        "message": "invalid block payload"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
curl -X POST https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlock",
    "params": [
      "0xf90217a0...SERIALIZED_BLOCK_PAYLOAD",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY');

// First, obtain the serialized block payload from your tracing workflow
// Then trace all transactions in the block
const blockRlp = '0xf90217a0...'; // Serialized block payload

// Trace with call tracer
const traces = await provider.send('debug_traceBlock', [
  blockRlp,
  { tracer: 'callTracer' }
]);

for (const trace of traces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
}

// Trace with default opcode tracer (verbose output)
const opcodeTraces = await provider.send('debug_traceBlock', [
  blockRlp,
  { disableStorage: true, disableStack: false }
]);

for (const trace of opcodeTraces) {
  console.log(`Tx: ${trace.txHash}, Opcodes: ${trace.result.structLogs.length}`);
}
```

```python
import requests
import json

def trace_block_by_rlp(rlp_data, tracer='callTracer'):
    response = requests.post(
        'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlock',
            'params': [rlp_data, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

# debug_traceBlock - BSC RPC Method
block_rlp = '0xf90217a0...'  # Serialized block payload
traces = trace_block_by_rlp(block_rlp)

for trace in traces:
    tx_hash = trace.get('txHash', 'unknown')
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    print(f'Tx {tx_hash}: {result["type"]} | Gas: {gas_used}')

    # Print sub-calls
    for call in result.get('calls', []):
        print(f'  -> {call["type"]} to {call["to"]}')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlock', [
    block_rlp,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
)

type TraceResult struct {
    TxHash string      `json:"txHash"`
    Result CallTrace   `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Calls   []CallTrace `json:"calls"`
}

func main() {
    blockRlp := "0xf90217a0..." // Serialized block payload

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlock",
        "params":  []interface{}{blockRlp, map[string]string{"tracer": "callTracer"}},
        "id":      1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post("https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY", "application/json", bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    for _, trace := range response.Result {
        fmt.Printf("Tx: %s | Type: %s | Gas: %s\n",
            trace.TxHash, trace.Result.Type, trace.Result.GasUsed)
    }
}
```

## Common Use Cases

### 1. Block-Level Gas Profiling

Analyze gas consumption across all transactions in a block on BSC:

```javascript
async function profileBlockGas(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  let totalGas = 0;
  const txGas = [];

  for (const trace of traces) {
    const gasUsed = parseInt(trace.result.gasUsed, 16);
    totalGas += gasUsed;
    txGas.push({
      txHash: trace.txHash,
      gasUsed,
      type: trace.result.type,
      hasSubCalls: (trace.result.calls || []).length > 0
    });
  }

  // Sort by gas usage
  txGas.sort((a, b) => b.gasUsed - a.gasUsed);

  console.log(`Block total gas: ${totalGas}`);
  console.log('Top gas consumers:');
  for (const tx of txGas.slice(0, 5)) {
    const pct = ((tx.gasUsed / totalGas) * 100).toFixed(1);
    console.log(`  ${tx.txHash}: ${tx.gasUsed} gas (${pct}%)`);
  }

  return { totalGas, txGas };
}
```

### 2. MEV Detection and Analysis

Detect sandwich attacks and arbitrage in BSC blocks:

```javascript
async function detectMEVPatterns(provider, blockRlp) {
  const traces = await provider.send('debug_traceBlock', [
    blockRlp,
    { tracer: 'callTracer' }
  ]);

  const dexInteractions = [];

  for (let i = 0; i < traces.length; i++) {
    const trace = traces[i];
    const calls = flattenCalls(trace.result);

    for (const call of calls) {
      // Detect swap-like function selectors (e.g., Uniswap swapExactTokensForTokens)
      if (call.input && call.input.startsWith('0x38ed1739')) {
        dexInteractions.push({
          index: i,
          txHash: trace.txHash,
          to: call.to,
          type: 'swap'
        });
      }
    }
  }

  // Check for sandwich patterns (swap-X-swap by same sender)
  for (let i = 0; i < dexInteractions.length - 2; i++) {
    const first = dexInteractions[i];
    const last = dexInteractions[i + 2];
    if (first.txHash !== last.txHash &&
        traces[first.index].result.from === traces[last.index].result.from) {
      console.log(`Potential sandwich: tx ${first.index} and ${last.index}`);
    }
  }

  return dexInteractions;
}

function flattenCalls(trace) {
  const calls = [trace];
  for (const sub of trace.calls || []) {
    calls.push(...flattenCalls(sub));
  }
  return calls;
}
```

### 3. Comparing Block Execution Across Clients

Verify consistent execution by tracing the same block RLP on different clients:

```python
import requests

def trace_on_endpoint(endpoint, block_rlp):
    response = requests.post(endpoint, json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlock',
        'params': [block_rlp, {'tracer': 'callTracer'}],
        'id': 1
    })
    return response.json()['result']

# Compare traces from two different endpoints
block_rlp = '0xf90217a0...'
traces_a = trace_on_endpoint('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY', block_rlp)
traces_b = trace_on_endpoint('https://other-endpoint.example.com', block_rlp)

# Verify same number of traces
assert len(traces_a) == len(traces_b), 'Transaction count mismatch'

# Compare gas usage per transaction
for i, (a, b) in enumerate(zip(traces_a, traces_b)):
    gas_a = int(a['result']['gasUsed'], 16)
    gas_b = int(b['result']['gasUsed'], 16)
    if gas_a != gas_b:
        print(f'Gas mismatch at tx {i}: {gas_a} vs {gas_b}')
    else:
        print(f'Tx {i}: {gas_a} gas (consistent)')
```

## Related Methods

- [`debug_traceBlockByHash`](https://www.dwellir.com/docs/bsc/debug_traceBlockByHash) - Trace all transactions in a block by hash (more commonly used)
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/bsc/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/bsc/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/bsc/debug_traceCall) - Trace a call without creating a transaction

---

## debug_traceBlockByHash - BSC RPC Method

Traces all transactions in a block on Binance Smart Chain identified by its block hash. Returns detailed execution traces for every transaction, making it ideal for investigating specific blocks when you know the exact hash.

> **Why BSC?** Build on the third-largest blockchain by market cap with $12B+ TVL and 37%+ DEX market share with sub-$0.10 fees, 2.6M daily active users, full EVM compatibility, and direct Binance integration.

BSC API endpoints are full nodes with debug APIs enabled. Debug methods work for blocks and transactions whose state is still retained on the node. Older historical state requires an archive node, available as a dedicated node or dedicated cluster.

## When to Use This Method

`debug_traceBlockByHash` is essential for DeFi developers, trading platform builders, and teams seeking Binance ecosystem access:

- **Investigating Specific Blocks** - When you have a block hash from an event, alert, or on-chain reference, trace every transaction in that exact block on BSC
- **Analyzing Transaction Execution Order** - Understand how transactions within a block interact, including cross-transaction state dependencies
- **Debugging Reverted Transactions** - Find the exact opcode where transactions failed across an entire block for high-frequency DeFi (PancakeSwap), NFT marketplaces, and GameFi applications
- **Fork and Reorg Analysis** - Use block hashes to trace transactions in specific forks, ensuring you analyze the correct chain branch

## Best Practices

- Use block hash for deterministic results during chain reorganizations
- Same performance considerations as debug\_traceBlockByNumber apply
- Requires archive node access; not available on standard full nodes

## Request Parameters

- `blockHash` (`DATA, required`): 32-byte hash of the block to trace
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `address` (`Object, required`): State of each account touched by the transaction
- `address.balance` (`QUANTITY, required`): Account balance before execution
- `address.nonce` (`QUANTITY, required`): Account nonce before execution
- `address.code` (`DATA, required`): Contract bytecode (if contract account)
- `address.storage` (`Object, required`): Storage slots read or written

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "txHash": "0xabc123...",
      "result": {
        "type": "CALL",
        "from": "0x1234567890abcdef1234567890abcdef12345678",
        "to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "value": "0xde0b6b3a7640000",
        "gas": "0x76c0",
        "gasUsed": "0x5208",
        "input": "0x",
        "output": "0x"
      }
    },
    {
      "txHash": "0xdef456...",
      "result": {
        "type": "CALL",
        "from": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
        "to": "0x1234567890abcdef1234567890abcdef12345678",
        "value": "0x0",
        "gas": "0x1e848",
        "gasUsed": "0xb841",
        "input": "0xa9059cbb...",
        "output": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "calls": [
          {
            "type": "STATICCALL",
            "from": "0x1234567890abcdef1234567890abcdef12345678",
            "to": "0xfedcba0987654321fedcba0987654321fedcba09",
            "gas": "0x1a5b4",
            "gasUsed": "0x1388",
            "input": "0x70a08231...",
            "output": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000"
          }
        ]
      }
    }
  ]
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "block not found"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# debug_traceBlockByHash - BSC RPC Method
curl -X POST https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff",
      {"tracer": "callTracer"}
    ],
    "id": 1
  }'

# Trace with prestate tracer
curl -X POST https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "debug_traceBlockByHash",
    "params": [
      "0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff",
      {"tracer": "prestateTracer"}
    ],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY');
const blockHash = '0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff';

// Call tracer - shows internal calls tree
const callTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'callTracer' }
]);

console.log(`Block has ${callTraces.length} transactions`);

for (const trace of callTraces) {
  console.log(`Tx: ${trace.txHash}`);
  console.log(`  Type: ${trace.result.type}`);
  console.log(`  From: ${trace.result.from}`);
  console.log(`  To: ${trace.result.to}`);
  console.log(`  Gas used: ${parseInt(trace.result.gasUsed, 16)}`);
  if (trace.result.error) {
    console.log(`  ERROR: ${trace.result.error}`);
  }
}

// Prestate tracer - shows account state before execution
const prestateTraces = await provider.send('debug_traceBlockByHash', [
  blockHash,
  { tracer: 'prestateTracer' }
]);

for (const trace of prestateTraces) {
  const addresses = Object.keys(trace.result);
  console.log(`Tx ${trace.txHash} touched ${addresses.length} accounts`);
}
```

```python
import requests

def trace_block_by_hash(block_hash, tracer='callTracer'):
    response = requests.post(
        'https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY',
        json={
            'jsonrpc': '2.0',
            'method': 'debug_traceBlockByHash',
            'params': [block_hash, {'tracer': tracer}],
            'id': 1
        }
    )
    return response.json()['result']

block_hash = '0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff'

# Call tracer
traces = trace_block_by_hash(block_hash)
print(f'Block contains {len(traces)} transactions')

for trace in traces:
    result = trace['result']
    gas_used = int(result['gasUsed'], 16)
    status = 'REVERTED' if 'error' in result else 'OK'
    print(f'  {trace["txHash"]}: {gas_used} gas [{status}]')

# Using web3.py
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY'))

traces = w3.provider.make_request('debug_traceBlockByHash', [
    block_hash,
    {'tracer': 'callTracer'}
])
print(f'Traced {len(traces["result"])} transactions')
```

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
)

type TraceResult struct {
    TxHash string    `json:"txHash"`
    Result CallTrace `json:"result"`
}

type CallTrace struct {
    Type    string      `json:"type"`
    From    string      `json:"from"`
    To      string      `json:"to"`
    Value   string      `json:"value"`
    Gas     string      `json:"gas"`
    GasUsed string      `json:"gasUsed"`
    Input   string      `json:"input"`
    Output  string      `json:"output"`
    Error   string      `json:"error,omitempty"`
    Calls   []CallTrace `json:"calls,omitempty"`
}

func main() {
    blockHash := "0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff"

    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "debug_traceBlockByHash",
        "params": []interface{}{
            blockHash,
            map[string]string{"tracer": "callTracer"},
        },
        "id": 1,
    }

    body, _ := json.Marshal(payload)
    resp, err := http.Post(
        "https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY",
        "application/json",
        bytes.NewReader(body),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    data, _ := io.ReadAll(resp.Body)

    var response struct {
        Result []TraceResult `json:"result"`
    }
    json.Unmarshal(data, &response)

    fmt.Printf("Block contains %d transactions\n", len(response.Result))
    for _, trace := range response.Result {
        gasUsed, _ := strconv.ParseInt(trace.Result.GasUsed[2:], 16, 64)
        status := "OK"
        if trace.Result.Error != "" {
            status = "REVERTED: " + trace.Result.Error
        }
        fmt.Printf("  %s: %d gas [%s]\n", trace.TxHash, gasUsed, status)
    }
}
```

## Common Use Cases

### 1. Find All Reverted Transactions in a Block

Identify and analyze failed transactions on BSC:

```javascript
async function findReverts(provider, blockHash) {
  const traces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'callTracer' }
  ]);

  const reverts = [];

  for (const trace of traces) {
    if (trace.result.error) {
      reverts.push({
        txHash: trace.txHash,
        error: trace.result.error,
        revertReason: trace.result.revertReason || 'N/A',
        from: trace.result.from,
        to: trace.result.to,
        gasUsed: parseInt(trace.result.gasUsed, 16)
      });
    }

    // Also check sub-calls for internal reverts
    const internalReverts = findInternalReverts(trace.result.calls || []);
    if (internalReverts.length > 0) {
      reverts.push({
        txHash: trace.txHash,
        internalReverts,
        topLevelSuccess: !trace.result.error
      });
    }
  }

  console.log(`Found ${reverts.length} reverted transactions out of ${traces.length}`);
  for (const r of reverts) {
    console.log(`  ${r.txHash}: ${r.error || 'internal revert'}`);
  }
  return reverts;
}

function findInternalReverts(calls) {
  const reverts = [];
  for (const call of calls) {
    if (call.error) {
      reverts.push({ type: call.type, to: call.to, error: call.error });
    }
    reverts.push(...findInternalReverts(call.calls || []));
  }
  return reverts;
}
```

### 2. Analyze Token Transfer Patterns in a Block

Extract all ERC-20 transfer events from block traces on BSC:

```python
import requests

def analyze_token_transfers(block_hash):
    response = requests.post('https://api-bsc-mainnet-full.n.dwellir.com/YOUR_API_KEY', json={
        'jsonrpc': '2.0',
        'method': 'debug_traceBlockByHash',
        'params': [block_hash, {'tracer': 'callTracer'}],
        'id': 1
    })
    traces = response.json()['result']

    # ERC-20 transfer(address,uint256) selector
    TRANSFER_SELECTOR = '0xa9059cbb'
    # ERC-20 transferFrom(address,address,uint256) selector
    TRANSFER_FROM_SELECTOR = '0x23b872dd'

    transfers = []

    for trace in traces:
        calls = flatten_calls(trace['result'])
        for call in calls:
            input_data = call.get('input', '')
            if input_data.startswith(TRANSFER_SELECTOR) or \
               input_data.startswith(TRANSFER_FROM_SELECTOR):
                transfers.append({
                    'tx_hash': trace['txHash'],
                    'token_contract': call['to'],
                    'from': call['from'],
                    'type': call['type'],
                    'gas_used': int(call.get('gasUsed', '0x0'), 16)
                })

    print(f'Found {len(transfers)} token transfers in block')
    # Group by token contract
    by_token = {}
    for t in transfers:
        by_token.setdefault(t['token_contract'], []).append(t)

    for token, txs in by_token.items():
        print(f'  {token}: {len(txs)} transfers')

    return transfers

def flatten_calls(trace):
    calls = [trace]
    for sub in trace.get('calls', []):
        calls.extend(flatten_calls(sub))
    return calls

analyze_token_transfers('0xc2b1f9c182513683b2358397114147563e5eb5a0be5d153487f15bb32ea559ff')
```

### 3. Block Execution State Diff

Compare account states before and after block execution using the prestate tracer:

```javascript
async function getBlockStateDiff(provider, blockHash) {
  // Get prestate - accounts state before each transaction
  const prestateTraces = await provider.send('debug_traceBlockByHash', [
    blockHash,
    { tracer: 'prestateTracer', tracerConfig: { diffMode: true } }
  ]);

  const allAddresses = new Set();
  const balanceChanges = {};

  for (const trace of prestateTraces) {
    const pre = trace.result.pre || trace.result;
    const post = trace.result.post || {};

    for (const [addr, state] of Object.entries(pre)) {
      allAddresses.add(addr);
      if (!balanceChanges[addr]) {
        balanceChanges[addr] = {
          preBal: BigInt(state.balance || '0x0'),
          postBal: BigInt((post[addr]?.balance) || state.balance || '0x0')
        };
      }
    }
  }

  console.log(`Block touched ${allAddresses.size} unique addresses`);
  for (const [addr, change] of Object.entries(balanceChanges)) {
    const diff = change.postBal - change.preBal;
    if (diff !== 0n) {
      console.log(`  ${addr}: ${diff > 0n ? '+' : ''}${diff} wei`);
    }
  }

  return balanceChanges;
}
```

## Related Methods

- [`debug_traceBlock`](https://www.dwellir.com/docs/bsc/debug_traceBlock) - Trace all transactions using RLP-encoded block data
- [`debug_traceBlockByNumber`](https://www.dwellir.com/docs/bsc/debug_traceBlockByNumber) - Trace all transactions in a block by number
- [`debug_traceTransaction`](https://www.dwellir.com/docs/bsc/debug_traceTransaction) - Trace a single transaction by hash
- [`debug_traceCall`](https://www.dwellir.com/docs/bsc/debug_traceCall) - Trace a call without creating a transaction
- [`eth_getBlockByHash`](https://www.dwellir.com/docs/bsc/eth_getBlockByHash) - Get block details by hash (without traces)

---

## debug_traceBlockByNumber - BSC RPC Method

Traces all transactions in a block on Binance Smart Chain identified by its block number or tag. This is the most convenient block-tracing method - pass a block number or `"latest"` to get full execution traces of every transaction in that block.

> **Why BSC?** Build on the third-largest blockchain by market cap with $12B+ TVL and 37%+ DEX market share with sub-$0.10 fees, 2.6M daily active users, full EVM compatibility, and direct Binance integration.

BSC API endpoints are full nodes with debug APIs enabled. Debug methods work for blocks and transactions whose state is still retained on the node. Older historical state requires an archive node, available as a dedicated node or dedicated cluster.

## When to Use This Method

`debug_traceBlockByNumber` is essential for DeFi developers, trading platform builders, and teams seeking Binance ecosystem access:

- **Historical Block Analysis** - Trace transactions in any past block by number, enabling time-series analysis of BSC execution patterns
- **Gas Consumption Patterns** - Profile gas usage across all transactions in a block to understand network congestion and gas cost trends for high-frequency DeFi (PancakeSwap), NFT marketplaces, and GameFi applications
- **Debugging State Transitions** - Inspect how every transaction in a block changed the global state, useful for verifying protocol upgrades and hard fork behavior
- **Automated Block Scanning** - Iterate through block ranges by number to build analytics pipelines, detect anomalies, and index execution traces

## Best Practices

- Requires archive node access; not available on standard full nodes
- Use the callTracer for faster execution when full opcode detail is not needed
- A full trace of a dense block can be hundreds of megabytes in size
- Paginate results and process traces in batches for large blocks

## Request Parameters

- `blockNumber` (`QUANTITY|TAG, required`): Block number as hex string, or tag: "earliest", "latest", "pending"
- `tracerConfig` (`Object, optional`): Tracer configuration object (see options below)

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}
```

## Response Fields

- `result` (`Array<Object>, required`): Array of trace objects, one per transaction
- `result[].result` (`Object, required`): Trace output (structure depends on tracer used)
- `result[].txHash` (`DATA, required`): Transaction hash for this trace
- `type` (`string, required`): Call type (CALL, DELEGATECALL, STATICCALL, CREATE, CREATE2)
- `from` (`DATA, required`): Sender address
- `to` (`DATA, required`): Recipient address
- `value` (`QUANTITY, required`): Value transferred in wei
- `gas` (`QUANTITY, required`): Gas provided
- `gasUsed` (`QUANTITY, required`): Gas consumed
- `input` (`DATA, required`): Call data
- `output` (`DATA, required`): Return data
- `error` (`string, required`): Error message if the call reverted
- `revertReason` (`string, required`): Decoded revert reason (if available)
- `calls` (`Array, required`): Sub-calls made during execution
- `gas` (`QUANTITY, required`): Gas provided
- `returnValue` (`DATA, required`): Return value of the call
- `structLogs` (`Array, required`): Array of opcode execution steps
- `structLogs[].pc` (`QUANTITY, required`): Program counter
- `structLogs[].op` (`string, required`): Opcode name
- `structLogs[].gas` (`QUANTITY, required`): Remaining gas
- `structLogs[].gasCost` (`Q